// ************************************** Configuration ************************************* //
// **************************************** DPAP 平台常量配置 **************************************** //
Ext.define('Deppon.config.Dpap', {
	statics: {
		//静态资源地址
		RESOURCE_PATH: 'http://192.168.17.167/dpap/dpap',
		//用户无权限的异常CODE
		USER_NOTALLOW: 'ERROR.SECURITY.NOTALLOW'
	}
});

Ext.define('Deppon.config.Foss', {
	statics: {
		//静态资源地址
		RESOURCE_PATH: Deppon.config.Dpap.RESOURCE_PATH,
		// 设置查询用户信息与数据字典的时间（秒）
		UPDATE_INFO_INTERVAL: 300,
		// 问题上报的相关配置
		QUESTION_REPORT_URL: 'http://192.168.17.103:8080/itsm/question.do',
		QUESTION_REPORT_PARAMS: [ 'system', 'psnCode', 'subsystem', 'moduleName' ],
		getRequestUrl: function(url, params, paramValues) {
			if(Ext.isEmpty(params)) {
				return url;
			}
			url = url + '?';
			for(var i = 0; i < params.length; i++) {
				if(i == params.length-1) {
					url = url + params[i] + '=' + paramValues[i];
				} else {
					url = url + params[i] + '=' + paramValues[i] + '&';
				}
			}
			return url;
		}
	}
});
/**
 * 指定Extjs中占位图片的路径，代替Data URI Scheme
 */
Ext.BLANK_IMAGE_URL = Deppon.config.Foss.RESOURCE_PATH + '/images/default/tree/s.gif';

// ************************************** 处理浏览器兼容性问题 ************************************* //
/**
 * String对象的split方法在某些版本浏览器下不兼容
 */
var split;
// Avoid running twice; that would break the `nativeSplit` reference
split = split || function (undef) {

var nativeSplit = String.prototype.split,
    compliantExecNpcg = /()??/.exec("")[1] === undef, // NPCG: nonparticipating capturing group
    self;

self = function (str, separator, limit) {
    // If `separator` is not a regex, use `nativeSplit`
    if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
        return nativeSplit.call(str, separator, limit);
    }
    var output = [],
        flags = (separator.ignoreCase ? "i" : "") +
                (separator.multiline  ? "m" : "") +
                (separator.extended   ? "x" : "") + // Proposed for ES6
                (separator.sticky     ? "y" : ""), // Firefox 3+
        lastLastIndex = 0,
        // Make `global` and avoid `lastIndex` issues by working with a copy
        separator = new RegExp(separator.source, flags + "g"),
        separator2, match, lastIndex, lastLength;
    str += ""; // Type-convert
    if (!compliantExecNpcg) {
        // Doesn't need flags gy, but they don't hurt
        separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
    }
    /* Values for `limit`, per the spec:
     * If undefined: 4294967295 // Math.pow(2, 32) - 1
     * If 0, Infinity, or NaN: 0
     * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
     * If negative number: 4294967296 - Math.floor(Math.abs(limit))
     * If other: Type-convert, then use the above rules
     */
    limit = limit === undef ?
        -1 >>> 0 : // Math.pow(2, 32) - 1
        limit >>> 0; // ToUint32(limit)
    while (match = separator.exec(str)) {
        // `separator.lastIndex` is not reliable cross-browser
        lastIndex = match.index + match[0].length;
        if (lastIndex > lastLastIndex) {
            output.push(str.slice(lastLastIndex, match.index));
            // Fix browsers whose `exec` methods don't consistently return `undefined` for
            // nonparticipating capturing groups
            if (!compliantExecNpcg && match.length > 1) {
                match[0].replace(separator2, function () {
                    for (var i = 1; i < arguments.length - 2; i++) {
                        if (arguments[i] === undef) {
                            match[i] = undef;
                        }
                    }
                });
            }
            if (match.length > 1 && match.index < str.length) {
                Array.prototype.push.apply(output, match.slice(1));
            }
            lastLength = match[0].length;
            lastLastIndex = lastIndex;
            if (output.length >= limit) {
                break;
            }
        }
        if (separator.lastIndex === match.index) {
            separator.lastIndex++; // Avoid an infinite loop
        }
    }
    if (lastLastIndex === str.length) {
        if (lastLength || !separator.test("")) {
            output.push("");
        }
    } else {
        output.push(str.slice(lastLastIndex));
    }
    return output.length > limit ? output.slice(0, limit) : output;
};

// For convenience
String.prototype.split = function (separator, limit) {
    return self(this, separator, limit);
};

return self;
}();

/**
 * String对象的trim方法在某些版本浏览器下不兼容
 */
if (typeof String.prototype.trim !== "function") {
	String.prototype.trim = function() {
		return this.replace(/(^\s*)|(\s*$)/g, "");
	}
}
/**
 * Array对象的indexOf方法在某些版本浏览器下不兼容
 */
if(typeof Array.prototype.indexOf !== "function") {
    Array.prototype.indexOf = function(obj) {     
        for(var i = 0; i < this.length; i++) {
            if (this[i] == obj) {
                return i;
            }
        }
        return -1;
    }
}

// **************************************** Override **************************************** //
/**
 * form panel统一添加回车提交功能（暂时不上线）
 */
/*
Ext.override(Ext.form.Panel, {
	initComponent: function() {
        var me = this;
        if (me.frame) {
            me.border = false;
        }
        me.initFieldAncestor();
        me.callParent();
        me.relayEvents(me.form, [
            'beforeaction',
            'actionfailed',
            'actioncomplete',
            'validitychange',
            'dirtychange'
        ]);
        if (me.pollForChanges) {
            me.startPolling(me.pollInterval || 500);
        }
		
		var button = me.down('button[text="查询"]') || me.down('button[text="保存"]');
		if(button) {
			me.on('afterrender', me.onEnterKeyEvent, me);
		}
    },
	onEnterKeyEvent: function() {
		var me = this;
		me.keyNav = Ext.create('Ext.util.KeyNav', me.el, {
			enter: function(e) {
				var me = this,
					button;
				if(me.getForm().isValid()) {
					button = me.down('button[text="查询"]') || me.down('button[text="保存"]');
					if(button) {
						me.onTriggerClickEvent(button, e);
					}
				}
			},
			scope: me
		});
	},
	onTriggerClickEvent: function(scope, e) {
		var me = scope;
        if (me.preventDefault || (me.disabled && me.getHref()) && e) {
            e.preventDefault();
        }
        if (!me.disabled) {
            me.doToggle();
            me.maybeShowMenu();
            me.fireHandler(e);
        }
	}
});*/

/**
 * 修复页面缩小情况下horizontal scroll自动回滚至起始位置的Bug
 */
Ext.override(Ext.panel.Table, {
    onRestoreHorzScroll: function() {
        var left = this.scrollLeftPos;
        if (left) {
            this.syncHorizontalScroll(left, true);
        }
    },
	syncHorizontalScroll: function(left, setBody) {
        var me = this,
            scrollTarget;
        setBody = setBody === true;
        if (me.rendered && (setBody || left !== me.scrollLeftPos)) {
            if (setBody) {   
                scrollTarget = me.getScrollTarget();
                scrollTarget.el.dom.scrollLeft = left;
            }
            me.headerCt.el.dom.scrollLeft = left;
            me.scrollLeftPos = left;
        }
    }
});
/**
 * 修复target宽度设定机制导致的移动异常
 */
 /*
Ext.override(Ext.layout.container.Box, {
	// 增加targetElCls属性，设置targetEl的样式
    targetElCls: Ext.baseCSSPrefix + 'box-target',
	// 改写renderTpl，使targetEl的style属性设置变成class属性设置
    renderTpl: [
        '{%var oc,l=values.$comp.layout,oh=l.overflowHandler;',
        'if (oh.getPrefixConfig!==Ext.emptyFn) {',
            'if(oc=oh.getPrefixConfig())dh.generateMarkup(oc, out)',
        '}%}',
        '<div id="{ownerId}-innerCt" class="{[l.innerCls]} {[oh.getOverflowCls()]}" role="presentation">',
            '<div id="{ownerId}-targetEl" class="{targetElCls}">',
                '{%this.renderBody(out, values)%}',
            '</div>',
        '</div>',
        '{%if (oh.getSuffixConfig!==Ext.emptyFn) {',
            'if(oc=oh.getSuffixConfig())dh.generateMarkup(oc, out)',
        '}%}',
        {
            disableFormats: true,
            definitions: 'var dh=Ext.DomHelper;'
        }
    ],
	// 增加targetEl的宽度设置
    beginLayout: function (ownerContext) {
        var me = this,
            smp = me.owner.stretchMaxPartner,
            style = me.innerCt.dom.style,
            names = me.getNames();
        me.overflowHandler.beginLayout(ownerContext);
        if (typeof smp === 'string') {
            smp = Ext.getCmp(smp) || me.owner.query(smp)[0];
        }
        ownerContext.stretchMaxPartner = smp && ownerContext.context.getCmp(smp);
        me.callParent(arguments);
        ownerContext.innerCtContext = ownerContext.getEl('innerCt', me);
        me.scrollParallel = !!(me.owner.autoScroll || me.owner[names.overflowX]);
        me.scrollPerpendicular = !!(me.owner.autoScroll || me.owner[names.overflowY]);
        if (me.scrollParallel) {
            me.scrollPos = me.owner.getTargetEl().dom[names.scrollLeft];
        }
        style.width = '';
        style.height = '';
        me.cacheFlexes(ownerContext);
		// 设置targetEl的宽度
        me.targetEl.setWidth(20000);
    },
	finishedLayout: function(ownerContext) {
        this.overflowHandler.finishedLayout(ownerContext);
        this.callParent(arguments);
    },
	// 增加getRenderData方法，使用targetElCls
    getRenderData: function() {
        var data = this.callParent();
        data.targetElCls = this.targetElCls;
        return data;
    }
});*/
/**
 * 设置系统中window位置默认限制在它的父元素内
 */
Ext.override(Ext.window.Window, {
    constrain: true
});
/**
 * 设置系统中表格内的内容都可选择
 */
Ext.override(Ext.view.Table, {
    enableTextSelection: true
});
/**
 * 修改combo中工具栏的默认分页组件
 */
Ext.override(Ext.view.BoundList, {
	createPagingToolbar: function() {
        return Ext.widget('simplepaging', {
            id: this.id + '-paging-toolbar',
            pageSize: this.pageSize,
            store: this.store,
            border: false
        });
    }
});
/**
 * 修改fieldcontainer及其子类的beforeLabelTextTpl，并增加allowBlank属性，用以显示必填符号
 */
Ext.override(Ext.form.FieldContainer, {
	allowBlank: true,
	beforeLabelTextTpl: new Ext.XTemplate(
		'<tpl if="!allowBlank">',
			'<font color="red">*</font>',
		'</tpl>',
		'<tpl if="allowBlank">',
			'&nbsp;&nbsp;',
		'</tpl>'
	)
});
/**
 * 修复form中包含hiddenfield元素时，提示"maxLength is not defined"的Bug
 */
Ext.override(Ext.form.field.Base, {
    getSubTplData: function() {
        var data = this.callParent();
        if (!data.hasOwnProperty('maxLength')) {
            data.maxLength = undefined;
        }
        return data;
    }
});
/**
 * 修复grid中设置loadMask无法正常显示的bug
 */
Ext.override(Ext.view.AbstractView, {
    onRender: function() {
        var me = this;
        me.callParent();
        if (me.mask && Ext.isObject(me.store)) {
            me.setMaskBind(me.store);
        }
    }
});
/**
 * 修复日期组件日期格式为年月时，某些情况下显示不正确的Bug
 */
Ext.override(Ext.form.field.Date, {
    safeParse : function(value, format) {
        var me = this,
            utilDate = Ext.Date,
            result = null,
            strict = me.useStrict,
            parsedDate;

        if (utilDate.formatContainsHourInfo(format)) {
            result = utilDate.parse(value, format, strict);
		// ******************** Start ******************** //
        } else if(format == 'Y-m') {
			parsedDate = utilDate.parse(value + '-01 ' + me.initTime, format + '-d ' + me.initTimeFormat, strict);
			if (parsedDate) {
				result = utilDate.clearTime(parsedDate);
			}
		} else {
		// ********************* End ********************* //
            parsedDate = utilDate.parse(value + ' ' + me.initTime, format + ' ' + me.initTimeFormat, strict);
            if (parsedDate) {
                result = utilDate.clearTime(parsedDate);
            }
        }
        return result;
    }
});
/**
 * 修改textfield，使之值为空格时进行空值错误提醒
 */
Ext.override(Ext.form.field.Text, {
	getErrors: function(value) {
        var me = this,
            errors = [],
            validator = me.validator,
            emptyText = me.emptyText,
            allowBlank = me.allowBlank,
            vtype = me.vtype,
            vtypes = Ext.form.field.VTypes,
            regex = me.regex,
            format = Ext.String.format,
            msg;
        value = value || me.processRawValue(me.getRawValue());
        if (Ext.isFunction(validator)) {
            msg = validator.call(me, value);
            if (msg !== true) {
                errors.push(msg);
            }
        }
		// ******************** Start ******************** //
		var trimValue = value.toString().trim();
        if (trimValue.length < 1 || trimValue === emptyText) {
            if (!allowBlank) {
                errors.push(me.blankText);
            }
            return errors;
        }
		// ********************* End ********************* //
        if (value.length < me.minLength) {
            errors.push(format(me.minLengthText, me.minLength));
        }
        if (value.length > me.maxLength) {
            errors.push(format(me.maxLengthText, me.maxLength));
        }
        if (vtype) {
            if(!vtypes[vtype](value, me)){
                errors.push(me.vtypeText || vtypes[vtype +'Text']);
            }
        }
        if (regex && !regex.test(value)) {
            errors.push(me.regexText || me.invalidText);
        }
        return errors;
    }
});

/**
 * 修改Trigger及其子类使用setReadOnly时的样式，并解决设某些情况下，trigger没有隐藏显示的问题
 */
Ext.form.field.Trigger.override({
	setReadOnly: function(readOnly) {
        var me = this,
			hideTrigger = me.hideTrigger,
            inputEl = me.inputEl;
        readOnly = !!readOnly;
		if (readOnly == me.readOnly) {
			return;
        }
        me[readOnly ? 'addCls' : 'removeCls'](me.readOnlyCls);
		// ******************** Start ******************** //
		// 解决组件默认非只读状态，在调用setReadOnly方法设置只读时，trigger没有隐藏显示的问题
		if(!hideTrigger && me.triggerEl && me.triggerEl.elements) {
			var len = me.triggerEl.elements.length,
				i, el, parentEl;
			if(readOnly) {
				for(i=0; i<len; i++) {
					el = me.triggerEl.elements[i];
					parentEl = el.dom.parentElement;
					parentEl.style.display = "none"; 
					el.dom.style.display = "none";
				}
			} else {
				for(i=0; i<len; i++) {
					el = me.triggerEl.elements[i];
					parentEl = el.dom.parentElement;
					parentEl.style.display = "block"; 
					el.dom.style.display = "block";
				}
			}
		}
		// ********************* End ********************* //
        if (inputEl) {
            inputEl.dom.readOnly = readOnly;
			if(readOnly) {
				inputEl.dom.style.cursor = 'default';
				if(Ext.fly(inputEl.id + '-prefix') && Ext.fly(inputEl.id + '-prefix').dom){
					Ext.fly(inputEl.id + '-prefix').dom.innerHTML = '&nbsp;&nbsp;';
				}
				if(Ext.fly(me.id + '-errorEl') && Ext.fly(me.id + '-errorEl').dom) {
					Ext.fly(me.id + '-errorEl').dom.style.visibility = 'hidden';
				}
			} else {
				inputEl.dom.style.cursor = 'text';
				if(!me.allowBlank && Ext.fly(inputEl.id + '-prefix') && Ext.fly(inputEl.id + '-prefix').dom) {
					Ext.fly(inputEl.id + '-prefix').dom.innerHTML = '*';
				}
				if(Ext.fly(me.id + '-errorEl') && Ext.fly(me.id + '-errorEl').dom){
					Ext.fly(me.id + '-errorEl').dom.style.visibility = 'visible';
				}
			}
        } else if (me.rendering) {
            me.setReadOnlyOnBoxReady = true;
        }
		if (readOnly != me.readOnly) {
			me.readOnly = readOnly;
            me.updateLayout();
        }
		//修改numberfield的triger隐藏的时候，在显示位置错位的BUG
		/*if(!hideTrigger&&inputEl){
			var xtypeIndex = me.xtype.indexOf('numberfield'),
				itemId = me.id;
			if(xtypeIndex!=-1){
				var tableElWidth = Ext.get(itemId).getWidth(),
					lableElWidth = Ext.get(itemId+'-labelCell').getWidth(),
					trigerTableEl = Ext.get(itemId+'-triggerWrap'),
					trigerTabElWidth = trigerTableEl.getWidth(),
					width = tableElWidth-lableElWidth-17;
				trigerTableEl.setWidth(tableElWidth-lableElWidth-17);
			}
		}*/
    }
});
/**
 * 修改checkbox及其子类使用setReadOnly时的样式
 */
Ext.override(Ext.form.field.Checkbox, {
	selectReadOnlyCls: Ext.baseCSSPrefix + 'form-selection-readonly',
	beforeRender: function() {
        var me = this;
        me.callParent(arguments);
        me.beforeLabelableRender(arguments);
        if (me.readOnly) {
			me.removeCls(me.readOnlyCls);
			me.addCls(me.selectReadOnlyCls);
        }
    },
	setReadOnly: function(readOnly) {
        var me = this,
            inputEl = me.inputEl;
		readOnly = !!readOnly;
		me[readOnly ? 'addCls' : 'removeCls'](me.selectReadOnlyCls);
		me.readOnly = readOnly;
        if (inputEl) {
            inputEl.dom.disabled = !!readOnly || me.disabled;
			inputEl.dom.readOnly = readOnly;
        } else if (me.rendering) {
            me.setReadOnlyOnBoxReady = true;
        }
        me.fireEvent('writeablechange', me, readOnly);
    }
});
/**
 * 修改Ext.form.field.Base及其子类的必填样式、只读样式；增加提示功能(通过配置tipConfig)
 */
Ext.override(Ext.form.field.Base, {
	msgTarget: 'side',
	tipConfig: '',
	labelableRenderProps: 'readOnly,allowBlank,labelAlign,fieldBodyCls,baseBodyCls,clearCls,labelSeparator,msgTarget',
	beforeLabelTextTpl: new Ext.XTemplate(
		'<tpl if="!allowBlank">',
			'<tpl if="!readOnly">',
				'<label id="{inputId}-prefix" style="color:red">*</label>',
			'</tpl>',
			'<tpl if="readOnly">',
				'<label id="{inputId}-prefix" style="color:red">&nbsp;&nbsp;</label>',
			'</tpl>',
		'</tpl>',
		'<tpl if="allowBlank">',
			'<label id="{inputId}-prefix" style="color:red">&nbsp;&nbsp;</label>',
		'</tpl>'
	),
	isValid: function() {
        var me = this,
            disabled = me.disabled,
            validate = me.forceValidation || !disabled;
		if(me.readOnly && me.allowBlank) {
			return true;
		} else {
			return validate ? me.validateValue(me.processRawValue(me.getRawValue())) : disabled;
		}
    },
	setReadOnly: function(readOnly) {
        var me = this,
            inputEl = me.inputEl;
        readOnly = !!readOnly;
        me[readOnly ? 'addCls' : 'removeCls'](me.readOnlyCls);
        me.readOnly = readOnly;
        if (inputEl) {
            inputEl.dom.readOnly = readOnly;
			if(me.readOnly) {
				Ext.fly(inputEl.id + '-prefix').dom.innerHTML = '&nbsp;&nbsp;';
				if(Ext.fly(me.id + '-errorEl')) {
					Ext.fly(me.id + '-errorEl').dom.style.visibility = 'hidden';
				}
			} else {
				if(!me.allowBlank) {
					Ext.fly(inputEl.id + '-prefix').dom.innerHTML = '*';
				}
				Ext.fly(me.id + '-errorEl').dom.style.visibility = 'visible';
			}
        } else if (me.rendering) {
            me.setReadOnlyOnBoxReady = true;
        }
        me.fireEvent('writeablechange', me, readOnly);
    },
	initComponent : function() {
        var me = this;
        me.callParent();
        me.subTplData = me.subTplData || {};
        me.addEvents(
            'specialkey',
            'writeablechange'
        );
        me.initLabelable();
        me.initField();
        if (!me.name) {
            me.name = me.getInputId();
        }
		// ******************** Start ******************** //
		me.on('render',function(field) {
			if(field.tipConfig) {
				field.tipConfig.target = field.id;
				if(field.tipConfig.tipType != 'normal') {
					var depth = field.tipConfig.depth,
						cmp = field, tip;
					for(var i=0; i<depth; i++) {
						cmp = cmp.up();
					}
					field.tipConfig.data = cmp.tipData;
				}
				tip = Ext.create('Ext.tip.ToolTip', field.tipConfig);
				tip.on('beforeshow',function(tip, eOpts) {
					if(field.tipConfig.isShowByData){
						if(Ext.isEmpty(field.getValue())){
							return false;
						}
					}
				});
				if(field.tipConfig.tipBodyElement) {
					tip.bodyElement = null;
					tip.on('show',function(tip, eOpts) {
						var bodyElement = tip.bodyElement;
						if(!bodyElement) {
							bodyElement = Ext.create(field.tipConfig.tipBodyElement);
							bodyElement.render(tip.id+'-body');
							tip.bodyElement = bodyElement;
						}
						bodyElement.bindData(field.getValue());
					});
				}
			}
		});
		// ********************* End ********************* //
    }
});

/**
 * 解决在gird的选择模式为checkboxmodel下，当store改变的时候，表头checkbox的样式无变化和功能无效的问题
 */
Ext.override(Ext.selection.CheckboxModel, {
    renderer: function(value, metaData, record, rowIndex, colIndex, store, view) {
		// ******************** Start ******************** //
		//当beforerenderer监听事件返回true时，不显示checkbox列
		var me = this;
		if(me.hasListener('beforerenderer') && me.fireEvent('beforerenderer', record, store)) {
			me.unSelectRecords.push(record);
			return '<div>&#160;</div>';
		}
		// ********************* End ********************* //
        var baseCSSPrefix = Ext.baseCSSPrefix;
        metaData.tdCls = baseCSSPrefix + 'grid-cell-special ' + baseCSSPrefix + 'grid-cell-row-checker';
        return '<div class="' + baseCSSPrefix + 'grid-row-checker">&#160;</div>';
    },
	constructor: function() {
		// ******************** Start ******************** //
		//添加beforerenderer监听事件，定义unSelectRecords属性，用来记录不需显示checkbox列的记录
		var me = this;
        me.callParent(arguments);
		me.unSelectRecords = new Array();
		me.addEvents(
            'beforerenderer'
        );
		// ********************* End ********************* //
    },
    onRowMouseDown: function(view, record, item, index, e) {
        view.el.focus();
        var me = this,
            checker = e.getTarget('.' + Ext.baseCSSPrefix + 'grid-row-checker'),
            mode;
		// ******************** Start ******************** //
        //行不能选择checkbox列的时候，不用增加checkbox行选择的效果
		if(Ext.Array.contains(me.unSelectRecords, record) ) {
			return;
		}
		// ********************* End ********************* //
        if (!me.allowRightMouseSelection(e)) {
            return;
        }
        if (me.checkOnly && !checker) {
            return;
        }
        if (checker) {
            mode = me.getSelectionMode();
            if (mode !== 'SINGLE') {
                me.setSelectionMode('SIMPLE');
            }
            me.selectWithEvent(record, e);
            me.setSelectionMode(mode);
        } else {
            me.selectWithEvent(record, e);
        }
    },
    onHeaderClick: function(headerCt, header, e) {
        if (header.isCheckerHd) {
            e.stopEvent();
            var me = this,
                isChecked = header.el.hasCls(Ext.baseCSSPrefix + 'grid-hd-checker-on');
            me.preventFocus = true;
            if (isChecked) {
                me.deselectAll();
            } else {
                me.selectAll();
				// ******************** Start ******************** //
				//点击checkbox header全选时，取消选中无checkbox列的行
				if(me.unSelectRecords.length > 0) {
					me.deselect(me.unSelectRecords);
					header.el.addCls(Ext.baseCSSPrefix + 'grid-hd-checker-on');
				}
				// ********************* End ********************* //
            }
            delete me.preventFocus;
			// ******************** Start ******************** //
			//修复点击全选时，store改变后会造成checkbox header仍然选中且无法正常使用的Bug
        	var selections =  me.selected.getCount(),
	    		storeCount = me.store.getCount(),
	            view = me.views[0],
				headerCt = view.headerCt,
				checkHd = headerCt.child('gridcolumn[isCheckerHd]');
	    	if(selections != storeCount) {
				if (isChecked) {
					checkHd.el.removeCls(this.checkerOnCls);
				}
			}
			// ********************* End ********************* //
        }
    }
});
/**
 * 解决在gird的选择模式为rowmodel下，展开行中的文本框光标不可左右移动的问题
 */
Ext.override(Ext.selection.RowModel, {
    onKeyLeft: function(e) {
        this.scrollByDeltaX(-this.deltaScroll);
		//增加返回true,让事件继续走下去
		return true;
    },

    onKeyRight: function(e) {
        this.scrollByDeltaX(this.deltaScroll);
		//增加返回true,让事件继续走下去
		return true;
    }
});
/**
 * 解决表格头点击之后，没有获得焦点的问题
 */
Ext.override(Ext.grid.header.Container, {
    onHeaderClick: function(header, e, t) {
        header.focus();
        header.fireEvent('headerclick', this, header, e, t);
        this.fireEvent("headerclick", this, header, e, t);
    }
});
/**
 * 重写EXTJS 4.1中的conection类中onComplete方法，使架构异常得到解决
 * 重写onUploadComplete解决文件上传时候可以以JSON返回结果
 * 增加onJsonResponse方法，进行JSON返回结果的处理
 */
Ext.override(Ext.data.Connection, {
	onJsonResponse: function(contentType, response, options){
		if(contentType.toLowerCase()=='application/json;charset=utf-8') {
			var json = Ext.decode(response.responseText);
			if(json) {
				//业务执行成功
				if (json.success && !json.isException) {
					Ext.callback(options.success, options.scope, [response, options]);
				//业务异常
				} else if (!json.success && !json.isException) {
					Ext.callback(options.exception, options.scope, [response, options]);
				//未知异常
				} else if (!json.success && json.isException) {
					if(json.stackTrace && json.stackTrace.indexOf("UserNotLoginException") > 0) {
						Deppon.errorMessage.Box.showGoLoginWin();
						return;
					}
					if(json.errorCode==Deppon.config.Dpap.USER_NOTALLOW){
						Ext.ux.Toast.msg('', json.message, 'error');
					}else{
						var ErrorMessageWindow = Ext.create('Deppon.errorMessage.Window',{
							message: json.message,
							stackTrace: json.stackTrace
						});
						ErrorMessageWindow.show();
					}
					Ext.callback(options.unknownException, options.scope, [response, options]);
				}
			}
		} else {
			if(response.responseText != null) {
					if(response.responseText.indexOf('<meta content="LOGIN_JSP">') > 0 ) {
						Deppon.errorMessage.Box.showGoLoginWin();
						return;
					}
			}
			Ext.callback(options.success, options.scope, [response, options]);
		}
	},
	onUploadComplete: function(frame, options) {
        var me = this,
            response = {
                responseText: '',
                responseXML: null
            }, contentType, doc, firstChild,
			flag = false;
        try {
            doc = frame.contentWindow.document || frame.contentDocument || window.frames[frame.id].document;
            if (doc) {
                if (doc.body) {
					//更新textarea为pre
                    if (/pre/i.test((firstChild = doc.body.firstChild || {}).tagName)) { // json response wrapped in pre
                        response.responseText = firstChild.innerHTML;
						contentType = 'application/json;charset=utf-8';
                    } else {
                        response.responseText = doc.body.innerHTML;
						contentType= 'text/html'
                    }
                }
                response.responseXML = doc.XMLDocument || doc;
				me.onJsonResponse(contentType, response, options);
				flag = true;
            }
        } catch (e) {
			console.log(e);
		}
        me.fireEvent('requestcomplete', me, response, options);
		if(!flag){
			Ext.callback(options.callback, options.scope, [options, true, response]);
		}
        setTimeout(function() {
            Ext.removeNode(frame);
        }, 100);
    },
    onComplete: function(request) {
        var me = this,
			options = request.options,
			result, success, response;
        try {
            result = me.parseStatus(request.xhr.status);
        } catch(e) {
            result = {
                success: false,
                isException: false
            };
        }
        success = result.success;
        if (success) {
            response = me.createResponse(request);
            me.fireEvent('requestcomplete', me, response, options);
			// ******************** Start ******************** //
			me.onJsonResponse(response.getResponseHeader('Content-Type'), response, options);
			// ********************* End ********************* //
        } else {
            if (result.isException || request.aborted || request.timedout) {
                response = me.createException(request);
            } else {
                response = me.createResponse(request);
            }
            me.fireEvent('requestexception', me, response, options);
            Ext.callback(options.failure, options.scope, [response, options]);
			// ******************** Start ******************** //
			//服务端异常提示
            Ext.MessageBox.show({
                title: Deppon.errorMessage.Box.title,
                msg: Deppon.errorMessage.Box.msg,
                buttons: Ext.MessageBox.OK,
                icon: Ext.MessageBox.ERROR
            });
			// ********************* End ********************* //
        }
        Ext.callback(options.callback, options.scope, [options, success, response]);
        delete me.requests[request.id];
        return response;
    }
});

/**
 * 重写EXTJS 4.1中的Action类中createCallback方法，使form submit提交的时候，如果出现架构异常，可以在页面上得到解决
 */
Ext.override(Ext.form.action.Action, {
	createCallback: function() {
        var me = this,
            undef,
            form = me.form;
        return {
            success: me.onSuccess,
            failure: me.onFailure,
            exception: me.onSuccess,
            unknownException: me.onSuccess,
			callback: me.onSuccess,
            scope: me,
            timeout: (this.timeout * 1000) || (form.timeout * 1000),
            upload: form.fileUpload ? me.onSuccess : undef
        };
    }
});
/**
 * 重写EXTJS 4.1中的Basic类中afterAction方法，使form submit提交的时候，如果出现架构异常，可以在页面上得到解决
 */
Ext.override(Ext.form.Basic, {
	afterAction: function(action, success) {
        if (action.waitMsg) {
            var MessageBox = Ext.MessageBox,
                waitMsgTarget = this.waitMsgTarget;
            if (waitMsgTarget === true) {
                this.owner.el.unmask();
            } else if (waitMsgTarget) {
                waitMsgTarget.unmask();
            } else {
                MessageBox.updateProgress(1);
                MessageBox.hide();
            }
        }
        if (success) {
            if (action.reset) {
                this.reset();
            }
            Ext.callback(action.success, action.scope || action, [this, action]);
            this.fireEvent('actioncomplete', this, action);
        } else {
			if(!Ext.isEmpty(action)&&!Ext.isEmpty(action.response)&&!Ext.isEmpty(action.response.responseText)){
				var json = Ext.decode(action.response.responseText);
				//业务异常
				if (!json.isException) {
					Ext.callback(action.exception, action.scope || action, [this, action]);
				//未知异常
				} else if (json.isException) {
					Ext.callback(action.unknownException, action.scope || action, [this, action]);
				}
			}else{
				Ext.callback(action.failure, action.scope || action, [this, action]);				
			}
            this.fireEvent('actionfailed', this, action);
        }
    }
});
// *************************************** Functions *************************************** //
/**
 *	跨页选择
 */
/*
Ext.util.AcrossSelector = function(key, gridId) {
	var me = this,
		key = key || "",
		gridId = gridId || "";
	this.selections = new Ext.util.HashMap();
	this.select = function(model, record, index, eOpts) {
		var keyValue = record.get(key);
		if(!me.selections.containsKey(keyValue)) {
			me.selections.add(keyValue, "");
		}
	};
	this.deselect = function(model, record, index, eOpts) {
		var keyValue = record.get(key);
		if(me.selections.containsKey(keyValue)) {
			me.selections.removeAtKey(keyValue);
		}
	};
	this.beforeload = function(store, operation, eOpts) {
		var grid = Ext.getCmp(gridId);
		if(grid) {
			grid.getSelectionModel().removeListener('deselect', me.deselect);
		}
	};
	this.load = function(store, records, successful, eOpts) {
		var grid = Ext.getCmp(gridId);
		if(grid) {
			var keyValue, 
				array = [],
				grid = Ext.getCmp(gridId),
				selModel = grid.getSelectionModel();
			store.each(function(record) {
				keyValue = record.get(key);
				if(me.selections.containsKey(keyValue)) {
					array.push(record);
				}
			});
			selModel.select(array, true);
			selModel.addListener('deselect', me.deselect);
		}
	};
	this.getSelections = function() {
		return me.selections.getKeys();
	}
};*/
/**
 *	悬浮提示[待完善]
 */
Ext.ux.Toast = function() {
    var msgCt;
	function createBox(t, s, f) {
		var result;
		if(f == 'error') {
			result = ['<div class="msg">',
				'<div class="top"><div class="title">'+ t +'</div></div>',
				'<div class="middle"><div class="erroriconstyle"></div><div class="textpanelstyle"><p style="text-align:left">'+ s +'</p></div></div>',
				'<div class="bottom_error"></div>',
				'</div>'].join('');
		} else {
			result = ['<div class="msg">',
				'<div class="top"><div class="title">'+ t +'</div></div>',
				'<div class="middle"><div class="okiconstyle"></div><div class="textpanelstyle"><p style="text-align:left">'+ s +'</p></div></div>',
				'<div class="bottom_ok"></div>',
				'</div>'].join('');
		}
		return result;
    };
    return {
        msg: function(title, content, flag, delay) {
            if(!msgCt) {
                msgCt = Ext.DomHelper.insertFirst(document.body, {
					id: 'msg-div'
				}, true);
            }
			if(Ext.isEmpty(flag)) flag = 'ok';
			if(Ext.isEmpty(delay)) delay = 4000;
            var m = Ext.DomHelper.append(msgCt, {
				html: createBox(title, content, flag)
			}, true);
            m.hide();
            m.slideIn('t').ghost('t', { delay: delay, remove: true});
        },
		init: function() {
		
		}
	};
}();
/**
 *	动态加载JavaScript文件
 */
if(Deppon === undefined) { Ext.ns('Deppon'); };
Deppon.JsLoader = function() {
   this.load = function(url) {
		var me = this,
			scripts = document.getElementsByTagName("script"),
			len = scripts.length,
			i, script, head;
		for (i=0; i<len; i++) {
			if (scripts[i].src && scripts[i].src.indexOf(url) != -1) {
				me.onSuccess();
				return;
			}
		}
		script = document.createElement("script");
		script.type = "text/javascript";
		script.src = url;
		head = document.getElementsByTagName("head")[0];
		try {
			head.appendChild(script);
			script.onload = script.onreadystatechange = function() {
				if (script.readyState && script.readyState != 'loaded' && script.readyState != 'complete') return;
				script.onreadystatechange = script.onload = null; 
				me.onSuccess();
			}
		} catch(e) {
			if(typeof(me.onFailure) == 'function') {
				me.onFailure();
			} else {
				throw "Failed to load javaScript file dynamical!";
			}
		}
	}
};
/**
 * 修改date对象的编码方法，使之返回date的时间戳
 */
Ext.JSON.encodeDate = function(date) {
    var dateTime = date.getTime();
    return dateTime;
};
/**
 * Excel上传功能的前台封装方法[待完善]
 */
Ext.ns('Deppon.ux.ExcelExporter');
Deppon.ux.ExcelExporter = function(url, params, success, failure) {
	if(!Ext.fly('downloadAttachFileForm')) {
	    var form = document.createElement('form');
	    form.id = 'downloadAttachFileForm';
	    form.style.display = 'none';
	    document.body.appendChild(form);
	}
	Ext.Ajax.request({
	    url: url,
	    form: Ext.fly('downloadAttachFileForm'),
	    method: 'POST',
	    params: params,
	    isUpload: true,
	    success: success,
	    failure: failure
	});
};
/**
 * 日期类型数据转换器
 */
function dateConvert(value, record) {
	if (!Ext.isEmpty(value)) {
		var date = new Date(value);
		return date;
	} else {
		return null;
	}
};
/*
 *回收组件与组件绑定的数据
 * @param cmp 组件
 */
function cleanComponent(cmp){
	if(typeof cmp=="undefined"){
		return;
	}
	if(typeof cmp=="string"){
		cmp = Ext.ComponentManager.get(cmp);
		if(!cmp){
			//如果在DOM树上有该节点，进行DOM树节点的删除
			if(cmp!=null){
				var cmpDom = Ext.getDom(cmp);
				Ext.removeNode(cmpDom);
			}
			return;
		}
	}
	//清空ComponentManager内的注册项
	var cmpArray = cmp.removeAll();
	//清空store
	for(var i=0;i<cmpArray.length;i++){
		if(cmpArray[i].store){
			//cmpArray[i].store.removeAll();
			Ext.data.StoreManager.unregister(cmpArray[i].store);
		}
	}
	cmpArray = null;
	Ext.ComponentManager.unregister(cmp);
	//如果在DOM树上有该节点，进行DOM树节点的删除
	if(cmp.id!=null){
		var cmpDom = Ext.getDom(cmp.id);
		Ext.removeNode(cmpDom);
	}
};
/**
* 合并单元格
* @param {} grid  要合并单元格的grid对象
* @param {} cols  要合并哪几列 [1,2,4]
*/
function mergeCells(grid, cols) {
	var arrayTr = document.getElementById(grid.getId()+"-body").firstChild.firstChild.firstChild.getElementsByTagName('tr');
	//var arrayTr = document.getElementById(grid.getId()+"-body").firstChild.nextSibling;
	//var arrayTr = Ext.get(grid.getId()+"-body").first().first().first().select("tr");
	var trCount = arrayTr.length;
	var arrayTd;
	var td;
	var merge = function(rowspanObj, removeObjs) { //定义合并函数
		if(rowspanObj.rowspan != 1) {
			arrayTd = arrayTr[rowspanObj.tr].getElementsByTagName("td"); //合并行
			td = arrayTd[rowspanObj.td - 1];
			td.rowSpan = rowspanObj.rowspan;
			td.vAlign = "middle";        
			Ext.each(removeObjs, function(obj) { //隐身被合并的单元格
				arrayTd = arrayTr[obj.tr].getElementsByTagName("td");
				arrayTd[obj.td - 1].style.display = 'none';
			});
		}  
	};
	var rowspanObj = {}; //要进行跨列操作的td对象{tr:1,td:2,rowspan:5}
	var removeObjs = []; //要进行删除的td对象[{tr:2,td:2},{tr:3,td:2}]
	var col;

	Ext.each(cols, function(colIndex) { //逐列去操作tr
		var rowspan = 1;
		var divHtml = null;		//单元格内的数值
		for(var i=1; i<trCount; i++) {  //i=0表示表头等没用的行
			arrayTd = arrayTr[i].getElementsByTagName("td");
			var cold = 0;
			Ext.each(arrayTd, function(Td) {	//获取RowNumber列和check列
			if(Td.getAttribute("class").indexOf("x-grid-cell-special") != -1)
				cold++;                
			});
			col = colIndex + cold;	//跳过RowNumber列和check列
			if(!divHtml) {
				divHtml = arrayTd[col-1].innerHTML;
				rowspanObj = {tr: i, td: col, rowspan: rowspan}
			}else{
				var cellText = arrayTd[col-1].innerHTML;
				var addf = function() { 
					rowspanObj["rowspan"] = rowspanObj["rowspan"] + 1;
					removeObjs.push({tr: i,td: col});
					if(i == trCount-1) {
						merge(rowspanObj, removeObjs);//执行合并函数
					}
				};
				var mergef = function() {
					merge(rowspanObj, removeObjs);//执行合并函数
					divHtml = cellText;
					rowspanObj = { tr: i, td: col, rowspan: rowspan };
					removeObjs = [];
				};
				if(cellText == divHtml) {
					if(colIndex != 1) { 
					var leftDisplay=arrayTd[col-2].style.display;//判断左边单元格值是否已display
					if(leftDisplay == 'none') addf();  
					else mergef();              
				} else addf();                      
			} else mergef();      
		  }
		}
	});
};
/*
 * 设置表单里面的表单控件的编辑状态
 * @param form 表单控件
 * @param editable 是否可编辑(true or false)
 */
function setFormEditAble(form,editable){
	var fields = form.getForm().getFields();
	fields.each(function(field){
		field.setReadOnly(!editable);
	});
};
/**
 * 新建Tab功能
 * @param tID 待打开的tab id
 * @param tText tab显示文本值
 * @param tLoc tab页面请求的URL
 * @param params 请求的参数
 * @param loadTabFun 加载panel的function
*/
function addTab(tID, tText, tLoc, params, loadTabFun) {
	var tabPanel = Ext.getCmp('mainAreaPanel'),
		mainNav = Ext.getCmp('mainNav');
		tab = Ext.getCmp(tID);
	if(Ext.isEmpty(mainNav)) {
		var tLocStrs = tLoc.split('.');
			tLoc = tLocStrs[0] + '_include.';
		for(var i=1;i<tLocStrs.length;i++){
			tLoc = tLoc + tLocStrs[i];
		}
		GUI_Proxy.addTab(tID, tText, tLoc, params, loadTabFun);
		return;
	}
	if(params != "" && params != null ) {
		tabPanel.params = params;
	}
	if(!tab) {
		tabPanel.add({
			id: tID,
			title: tText,
			layout: 'fit',
			closable: true,
			tabConfig: { width: 180 },
			cls: 'autoHeight',
			bodyCls: 'autoHeight',
			loader: {
				scripts: true,
				autoLoad: true,
				params: params,
				url: tLoc,
				listeners: {
					'load': function(loader, response, options, eOpts){
						if(loadTabFun != null) {
							loadTabFun(loader.getTarget(), options.params);
						}
					}
				}
			}
		});
		tabPanel.setActiveTab(tabPanel.child('#'+tID));
	} else {
		tabPanel.setActiveTab(tab);
		if(loadTabFun != null) {
			loadTabFun(tab, params);
		}
	}
};
/**
 * 关闭Tab功能
 * @param tID 待关闭的tab id
*/
function removeTab(tID) {
	var tabPanel = Ext.getCmp('mainAreaPanel'),
		tab = Ext.getCmp(tID);
	if(tabPanel==null){
		return;
	}
	tabPanel.remove(tab, true);
};
// *************************************** Extensions *************************************** //
Ext.define('Deppon.errorMessage.Window', {
	extend: 'Ext.window.Window', 
	title: 'Server Exception',
	eTitle: 'Unkown Exception',
	eDetailTitle: 'Exception Detailed Information',
	bodyStyle: 'padding:5px 5px 0',
	resizable: false,
	modal: true,
	width: 550,
	defaults: {
		anchor: '100%',
		autoScroll: true
	},
	message : null,
	buttons: [{
		text: 'OK',
		handler: function(button, even) {
			button.up('window').destroy();
		}
	}],
	constructor: function(config) {
        var me = this,
			cfg = Ext.apply({}, config);
		me.message = config.message;
		me.stackTrace = config.stackTrace;
		me.items = [{
			xtype: 'panel',
			frame: true,
			title: me.eTitle,
			width: 500,
			html: me.message
		},{
			xtype: 'panel',
			frame: true,
			title: me.eDetailTitle,
			collapsible: true,
			collapsed: true,
			width: 500,
			autoScroll: true,
			items: [{
				height: 200,
				width: 500,
				html: me.stackTrace
			}]
		}];
		me.callParent([cfg]);
    }
});
Ext.define('Deppon.errorMessage.Box', {
	statics: {
		title: 'Notice:',
		msg: 'Server Connection Exception',
		goLoginMsg: 'Session is invalid, Please login again!',
		showGoLoginWin: function(){
			 Ext.MessageBox.show({
                title: Deppon.errorMessage.Box.title,
                msg: Deppon.errorMessage.Box.goLoginMsg,
                buttons: Ext.MessageBox.OK,
				fn: function(btn){
					window.location = '../login/index.action';
    			},
                icon: Ext.MessageBox.WARNING
            });
		}
	}
});

// **************************************** Vtypes **************************************** //
/**
 * @author xiantao zeng
 * @since 2012-12-14
 * @memo 单号格式验证vtype
 */
Ext.apply(Ext.form.field.VTypes, {
    order:  function(v, field) {
		// 自动清空汉字和圆角字符，enableKeyEvents须设置为true
		field.on('keyup', function(field, e) {
			var value = field.getValue();
			value = value.replace(/[^\x00-\xff]/gi, "");
			field.setValue(value);
		}, field);
		var minCharLength = field.minCharLength || 8,
			maxCharLength = field.maxCharLength || 10,
			reg;
		if(minCharLength > maxCharLength) {
			Ext.form.field.VTypes['orderText'] = '正则表达式错误！';
			return false;
		}
		reg = new RegExp("^[0-9]{"+minCharLength+","+maxCharLength+"}$");
		Ext.form.field.VTypes['orderText'] = '请输入正确的单号格式['+minCharLength+'~'+maxCharLength+'位数字]';
        return reg.test(v);
    },
    orderMask: /[0-9]/i 
});

Ext.apply(Ext.form.field.VTypes, {
    waybill:  function(v, field) {
		// 自动清空汉字和圆角字符，enableKeyEvents须设置为true
		field.on('keyup', function(field, e) {
			var value = field.getValue();
			value = value.replace(/[^\x00-\xff]/gi, "");
			field.setValue(value);
		}, field);
		var minCharLength = field.minCharLength || 8,
			maxCharLength = field.maxCharLength || 10,
			reg;
		if(minCharLength > maxCharLength) {
			Ext.form.field.VTypes['waybillText'] = '正则表达式错误！';
			return false;
		}
		reg = new RegExp("^[0-9]{"+minCharLength+","+maxCharLength+"}$");
		Ext.form.field.VTypes['waybillText'] = '请输入正确的单号格式['+minCharLength+'~'+maxCharLength+'位数字]';
        return reg.test(v);
    },
    waybillMask: /[0-9]/i 
});
//读取文件SingleSelector.js
/**
 *	@docauthor 曾宪涛 <rxjh2ht@163.com>
 *
 *	单选选择器，公共选择器之一。
 *
 *	封装combobox，提供触发按钮查询、回车查询、分页栏、条目模板自定义、查询参数定义等配置功能。
 *
 */
Ext.define('Deppon.selector.SingleSelector', {
	extend: 'Ext.form.ComboBox',
	alias: ['widget.singleselector','widget.dynamiccomboselector'],
	alternateClassName: ['Deppon.commonselector.DynamicComboSelector'],
	/**
     * @cfg {String} triggerCls
     * 触发按钮样式。
     */
	triggerCls: Ext.baseCSSPrefix + 'form-search-trigger',
	/**
     * @cfg {Number} listWidth
     * 下拉列表宽度。
     */
	listWidth: this.width,
	/**
     * @cfg {Boolean} multiSelect
     * 是否支持多选。
     */
	multiSelect: false,
	/**
     * @cfg {Boolean} isPaging
     * 是否支持分页。
     */
	isPaging: false,
	/**
     * @cfg {Boolean} isEnterQuery
     * 是否支持回车查询。
     */
	isEnterQuery: false,
	/**
     * @cfg {String} displayField
     * 显示的字段。
     */
	displayField: null,
	/**
     * @cfg {String} valueField
     * 显示字段对应的关键字。
     */
	valueField: null,
	/**
     * @cfg {String} showContent
     * 下拉条目的显示模板。
     */
	showContent: null,
	/**
     * @cfg {Object} queryParam
     * 查询参数。
     */
	queryParam: null,
	/**
     * @cfg {String} triggerAction
     * 触发动作。
     */
	triggerAction: 'query',
	/**
     * @cfg {Number} minChars
     * 查询条件显示结果的最小字符数。
     */
	minChars: 0,
	/**
     * @cfg {Boolean} hideTrigger
     * 是否隐藏触发按钮。
     */
	hideTrigger: false,
	/**
     * @cfg {Object/String} plugins
     * 插件对象或者ptype。
     */
	plugins: 'clearvalueplugin',
	/**
     * @cfg {Boolean} queryAllFlag
     * 是否支持查询全部。
     */
	queryAllFlag: true,
	/**
     * @cfg {Number} validValueLength
     * 查询条件的最小字符串长度。
     */
	validValueLength: 0,
	/**
     * @cfg {Object} listConfig
     * 下拉列表的配置对象。
	 *   - {@link Ext.view.BoundList#cls} - 默认为空。
     *   - {@link Ext.view.BoundList#emptyText} - 默认为空字符串。
     *   - {@link Ext.view.BoundList#itemSelector} - 默认为BoundList定义的字符串。
     *   - {@link Ext.view.BoundList#loadingText} - 默认为 "Loading..."
     *   - {@link Ext.view.BoundList#minWidth} - 默认为 70
     *   - {@link Ext.view.BoundList#maxWidth} - 默认为 `undefined`
     *   - {@link Ext.view.BoundList#maxHeight} - 默认为 `300`
     *   - {@link Ext.view.BoundList#resizable} - 默认为 `false`
     *   - {@link Ext.view.BoundList#shadow} - 默认为 `'sides'`
     *   - {@link Ext.view.BoundList#width} - 默认为 `undefined` （自动匹配宽度，如果combobox的{@link #matchFieldWidth}属性设置为true）
     */
	listConfig: {
		getInnerTpl: function () {
			var comboObj = this.up('combo'),
				content = comboObj.showContent,
				keyArr = comboObj.getKeyWords(content);
			if(keyArr.length == 2) {
				return Ext.String.format('<div class="deppon-combo-item"><h3><span>{{1}}</span></h3>{{0}}</div>', keyArr[0], keyArr[1]);
			} else {
				return this.up('combo').showContent;
			}
		}
	},
	/**
	 * @method
     * 获取下拉条目模板中的关键字。
	 * @param {String} 下拉条目模板。
     * @return {Array} 关键字数组。
     */
	getKeyWords: function(content) {
		var conArr = content.split("}"),
			conEle, result = [],
			conIndex;
		for(var i=0, len=conArr.length; i<len; i++) {
			conEle = conArr[i];
			conIndex = conEle.indexOf("{");
			if(conIndex != -1) {
				result.push(conEle.substring(conEle.indexOf("{") + 1, conEle.length));
			}
		}
		return result;
	},
	getValueModel: function () {
		var models = this.valueModels;
		if (Ext.isEmpty(models) && models.length > 0) {
			return models[0];
		} else {
			return null;
		}
	},
	/**
	 * @private
     * combobox的change监听事件。
     */
	getChange: function (combo, newValue, oldValue) {
		if (combo.isExpanded == true) {
			combo.collapse();
		}
	},
	/**
	 * @private
     * combobox的keypress监听事件。
     */
	getKeyPress: function (combo, event, eOpts) {
		var me = this;
		if (event.getKey() == event.ENTER ) {
			if(me.queryAllFlag) {
				me.enterQueryAction(combo);
			} else {
				if(Ext.isEmpty(me.getValue())) {
					me.setActiveErrors(["请输入查询条件!"]);
					this.doComponentLayout();
				} else {
					me.enterQueryAction(combo);
				}
			}
		}
	},
	/**
	 * @private
     * combobox的store对象的beforeLoad监听事件。
     */
	getBeforeLoad: function (st, operation, e) {
		var me = this,
			searchParams = operation.params;
		if (Ext.isEmpty(searchParams)) {
			searchParams = {};
			Ext.apply(operation, {
				params: searchParams
			});
		}
		searchParams[me.queryParam] = me.rawValue;
	},
	/**
	 * @protected
     * 初始化监听事件方法。
     */
	initEvents: function () {
		var me = this;
		me.callParent(arguments);
		if (me.isEnterQuery == true) {
			me.mon(me, 'change', me.getChange, me);
			me.mon(me, 'keypress', me.getKeyPress, me);
		}
		me.mon(me.store, 'beforeLoad', me.getBeforeLoad, me);
	},
	/**
	 * @private
     * 组件初始化方法。
     */
	initComponent: function () {
		var me = this;
		me.on('expand', function (combo, eOpts) {
			combo.getPicker().minWidth = me.listWidth;
			combo.getPicker().setWidth(me.listWidth);
		});
		if (me.isPaging == true) {
			me.pageSize = me.store.pageSize;
		}
		if (me.showContent == null) {
			me.showContent = '{' + me.displayField + '}';
		}
		if (me.isEnterQuery == true) {
			me.enableKeyEvents = true;
			me.queryDelay = 1000000;
		}
		this.callParent(arguments);
	},
	/**
	 * @method
     * combobox触发按钮的绑定事件。
     */
	onTriggerClick: function() {
		var me = this;
		if(me.queryAllFlag) {
			me.queryAction();
		} else {
			if(Ext.isEmpty(me.getValue())) {
				me.setActiveErrors(["请输入查询条件!"]);
				this.doComponentLayout();
			} else {
				me.queryAction();
			}
		}
    },
	/**
	 * @method
     * combobox回车键的监听事件。
     * @param {Ext.form.ComboBox} combobox组件。
     */
	enterQueryAction: function(combo) {
		var me = this,
			clearvalueplugin = me.getPlugin('clearvalueplugin'),
			value = combo.getValue();
		if(combo.validValueLength != 0 && (!Ext.isEmpty(value) && value.length < combo.validValueLength)) {
			combo.setActiveErrors(["请输入至少" + combo.validValueLength + "个字符!"]);
			combo.doComponentLayout();
			return;
		}
		combo.store.loadPage(1, {
			scope: this,
			callback: function (records, operation, success) {
				if (records.length > 0) {
					combo.expand();
				} else {
					operation.params[combo.queryParam] = "";
					combo.lastQuery = undefined;
				}
			}
		});
		if(!Ext.isEmpty(clearvalueplugin)){
			me.hasSearch = true;
			me.triggerCell.item(0).setDisplayed(true);
		}
	},
	/**
	 * @method
     * combobox查询事件。
     */
	queryAction: function() {
		var me = this,
			value;
		if (!me.readOnly && !me.disabled) {
			if (me.isExpanded) {
				me.collapse();
			} else {
				me.onFocus({});
				value = me.getValue();
				if(me.validValueLength != 0 && (!Ext.isEmpty(value) && value.length < me.validValueLength)) {
					me.setActiveErrors(["请输入至少" + me.validValueLength + "个字符!"]);
					me.doComponentLayout();
					return;
				}
				if (me.triggerAction === 'all') {
					me.doQuery(me.allQuery, true);
				} else {
					me.doQuery(me.getRawValue(), false, true);
				}
			}
			me.inputEl.focus();
		}
	}
});

//读取文件linkagecomboselector.js
/**
 *	@docauthor 曾宪涛 <rxjh2ht@163.com>
 *
 *	联动选择器，公共选择器之一。
 *
 */
Ext.define('Deppon.selector.LinkedSelector', {
	extend: 'Deppon.selector.SingleSelector',
	alias: ['widget.linkedselector','widget.linkagecomboselector'],
	alternateClassName: ['Deppon.commonselector.LinkageComboSelector'],
	/**
     * @cfg {Object} parentParamsAndItemIds
     * 参数和项目Id。
     */
	parentParamsAndItemIds: null,
	/**
     * @cfg {Array} eventType
     * 事件类型。
     */
	eventType: ['callparent'],
	/**
	 * @method
     * 获取焦点并加载数据源。
     */
	getFocus: function () {
		var me = this;
		me.on('focus', function () {
			me.setValue(null);
			me.store.loadPage(1, {
				scope: this,
				callback: function (records, operation, success) {
					if (records.length > 0) {
						me.expand();
					}
				}
			});
		});
	},
	/**
	 * @method
     * 依据父类选择器数据加载数据源。
     */
	getCallParent: function () {
		var me = this,
			cmp;
		if (!Ext.isEmpty(me.parentParamsAndItemIds)) {
			Ext.Object.each(me.parentParamsAndItemIds, function (queryParam, itemId, parentParamsAndItemIds) {
				cmp = me.up().items.get(itemId);
				if (!cmp.hasListener('select')) {
					cmp.on('select', function (combo) {
						me.setValue(null);
						me.store.loadPage(1, {
							scope: this,
							callback: function (records, operation, success) {
								me.focus(false, 100);
								me.expand();
							}
						});
					});
				}
			});
		}
	},
	/**
	 * @private
     * 组件初始化方法。
     */
	initComponent: function () {
		var me = this;
		me.on('boxready', function () {
			var callparent = null;
			for (var i = 0; i < me.eventType.length; i++) {
				if (me.eventType[i] == 'focus') {
					me.getFocus();
				} else if (me.eventType[i] == 'callparent') {
					callparent = 'callparent';
					me.getCallParent();
				}
			}
			if (callparent == 'callparent') {
				me.un('focus');
			}
		});
		this.callParent(arguments);
		me.store.on('beforeLoad', function (st, operation, e) {
			var cmp = null,
				searchParams = operation.params;
			if (Ext.isEmpty(searchParams)) {
				searchParams = {};
			}
			searchParams[me.queryParam] = me.rawValue;
			if (!Ext.isEmpty(me.parentParamsAndItemIds)) {
				Ext.Object.each(me.parentParamsAndItemIds, function (queryParam, itemId, parentParamsAndItemIds) {
					cmp = me.up().items.get(itemId);
					searchParams[queryParam] = cmp.getValue();
				});
			}
			Ext.apply(operation, {
				params: searchParams
			});
		});
	}
});

//读取文件boxselect.js
//多选择框
Ext.define('Ext.ux.form.field.BoxSelect', {
	extend: 'Ext.form.field.ComboBox',
	alias: ['widget.comboboxselect', 'widget.boxselect'],
	requires: ['Ext.selection.Model', 'Ext.data.Store'],
	multiSelect: true,
	forceSelection: true,
	createNewOnEnter: false,
	createNewOnBlur: false,
	encodeSubmitValue: false,
	triggerOnClick: true,
	stacked: false,
	pinList: true,
	filterPickList: false,
	selectOnFocus: true,
	grow: true,
	growMin: false,
	growMax: false,
	fieldSubTpl: ['<div id="{cmpId}-listWrapper" class="x-boxselect {fieldCls} {typeCls}">', '<ul id="{cmpId}-itemList" class="x-boxselect-list">', '<li id="{cmpId}-inputElCt" class="x-boxselect-input">', '<input id="{cmpId}-inputEl" type="{type}" ', '<tpl if="name">name="{name}" </tpl>', '<tpl if="value"> value="{[Ext.util.Format.htmlEncode(values.value)]}"</tpl>', '<tpl if="size">size="{size}" </tpl>', '<tpl if="tabIdx">tabIndex="{tabIdx}" </tpl>', '<tpl if="disabled"> disabled="disabled"</tpl>', 'class="x-boxselect-input-field {inputElCls}" autocomplete="off">', '</li>', '</ul>', '</div>', {
		compiled: true,
		disableFormats: true
	}],
	childEls: ['listWrapper', 'itemList', 'inputEl', 'inputElCt'],
	componentLayout: 'boxselectfield',
	initComponent: function () {
		var me = this,
			typeAhead = me.typeAhead;
		if (typeAhead && !me.editable) {
			Ext.Error.raise('If typeAhead is enabled the combo must be editable: true -- please change one of those settings.');
		}
		Ext.apply(me, {
			typeAhead: false
		});
		me.callParent();
		me.typeAhead = typeAhead;
		me.selectionModel = new Ext.selection.Model({
			store: me.valueStore,
			mode: 'MULTI',
			lastFocused: null,
			onSelectChange: function (record, isSelected, suppressEvent, commitFn) {
				commitFn();
			}
		});
		if (!Ext.isEmpty(me.delimiter) && me.multiSelect) {
			me.delimiterRegexp = new RegExp(String(me.delimiter).replace(/[$%()*+.?\[\\\]{|}]/g, "\\$&"));
		}
	},
	initEvents: function () {
		var me = this;
		me.callParent(arguments);
		if (!me.enableKeyEvents) {
			me.mon(me.inputEl, 'keydown', me.onKeyDown, me);
		}
		me.mon(me.inputEl, 'paste', me.onPaste, me);
		me.mon(me.listWrapper, 'click', me.onItemListClick, me);
		// I would prefer to use relayEvents here to forward these events on, but I want
		// to pass the field instead of exposing the underlying selection model
		me.mon(me.selectionModel, {
			'selectionchange': function (selModel, selectedRecs) {
				me.applyMultiselectItemMarkup();
				me.fireEvent('valueselectionchange', me, selectedRecs);
			},
			'focuschange': function (selectionModel, oldFocused, newFocused) {
				me.fireEvent('valuefocuschange', me, oldFocused, newFocused);
			},
			scope: me
		});
	},
	onBindStore: function (store, initial) {
		var me = this;
		if (store) {
			me.valueStore = new Ext.data.Store({
				model: store.model,
				proxy: {
					type: 'memory'
				}
			});
			me.mon(me.valueStore, 'datachanged', me.applyMultiselectItemMarkup, me);
			if (me.selectionModel) {
				me.selectionModel.bindStore(me.valueStore);
			}
		}
	},
	onUnbindStore: function (store) {
		var me = this,
			valueStore = me.valueStore;
		if (valueStore) {
			if (me.selectionModel) {
				me.selectionModel.setLastFocused(null);
				me.selectionModel.deselectAll();
				me.selectionModel.bindStore(null);
			}
			me.mun(valueStore, 'datachanged', me.applyMultiselectItemMarkup, me);
			valueStore.destroy();
			me.valueStore = null;
		}
		me.callParent(arguments);
	},
	createPicker: function () {
		var me = this,
			picker = me.callParent(arguments);
		me.mon(picker, {
			'beforerefresh': me.onBeforeListRefresh,
			scope: me
		});
		if (me.filterPickList) {
			picker.addCls('x-boxselect-hideselections');
		}
		return picker;
	},
	onDestroy: function () {
		var me = this;
		Ext.destroyMembers(me, 'valueStore', 'selectionModel');
		me.callParent(arguments);
	},
	getSubTplData: function () {
		var me = this,
			data = me.callParent(),
			isEmpty = me.emptyText && data.value.length < 1;
		if (isEmpty) {
			data.value = me.emptyText;
		} else {
			data.value = '';
		}
		data.inputElCls = data.fieldCls.match(me.emptyCls) ? me.emptyCls : '';
		return data;
	},
	afterRender: function () {
		var me = this;
		if (Ext.supports.Placeholder && me.inputEl && me.emptyText) {
			delete me.inputEl.dom.placeholder;
		}
		me.bodyEl.applyStyles('vertical-align:top');
		if (me.grow) {
			if (Ext.isNumber(me.growMin) && (me.growMin > 0)) {
				me.listWrapper.applyStyles('min-height:' + me.growMin + 'px');
			}
			if (Ext.isNumber(me.growMax) && (me.growMax > 0)) {
				me.listWrapper.applyStyles('max-height:' + me.growMax + 'px');
			}
		}
		if (me.stacked === true) {
			me.itemList.addCls('x-boxselect-stacked');
		}
		if (!me.multiSelect) {
			me.itemList.addCls('x-boxselect-singleselect');
		}
		me.applyMultiselectItemMarkup();
		me.callParent(arguments);
	},
	findRecord: function (field, value) {
		var ds = this.store,
			matches;
		if (!ds) {
			return false;
		}
		matches = ds.queryBy(function (rec, id) {
			return rec.isEqual(rec.get(field), value);
		});
		return (matches.getCount() > 0) ? matches.first() : false;
	},
	onLoad: function () {
		var me = this,
			valueField = me.valueField,
			valueStore = me.valueStore,
			changed = false;
		if (valueStore) {
			if (!Ext.isEmpty(me.value) && (valueStore.getCount() == 0)) {
				me.setValue(me.value, false, true);
			}
			valueStore.suspendEvents();
			valueStore.each(function (rec) {
				var r = me.findRecord(valueField, rec.get(valueField)),
					i = r ? valueStore.indexOf(rec) : -1;
				if (i >= 0) {
					valueStore.removeAt(i);
					valueStore.insert(i, r);
					changed = true;
				}
			});
			valueStore.resumeEvents();
			if (changed) {
				valueStore.fireEvent('datachanged', valueStore);
			}
		}
		me.callParent(arguments);
	},
	isFilteredRecord: function (record) {
		var me = this,
			store = me.store,
			valueField = me.valueField,
			storeRecord,
			filtered = false;
		storeRecord = store.findExact(valueField, record.get(valueField));
		filtered = ((storeRecord === -1) && (!store.snapshot || (me.findRecord(valueField, record.get(valueField)) !== false)));
		filtered = filtered || (!filtered && (storeRecord === -1) && (me.forceSelection !== true) && (me.valueStore.findExact(valueField, record.get(valueField)) >= 0));
		return filtered;
	},
	doRawQuery: function () {
		var me = this,
			rawValue = me.inputEl.dom.value;
		if (me.multiSelect) {
			rawValue = rawValue.split(me.delimiter).pop();
		}
		this.doQuery(rawValue, false, true);
	},
	onBeforeListRefresh: function () {
		this.ignoreSelection++;
	},
	onListRefresh: function () {
		this.callParent(arguments);
		if (this.ignoreSelection > 0) {
			--this.ignoreSelection;
		}
	},
	onListSelectionChange: function (list, selectedRecords) {
		var me = this,
			valueStore = me.valueStore,
			mergedRecords = [],
			i;
		if ((me.ignoreSelection <= 0) && me.isExpanded) {
			valueStore.each(function (rec) {
				if (Ext.Array.contains(selectedRecords, rec) || me.isFilteredRecord(rec)) {
					mergedRecords.push(rec);
				}
			});
			mergedRecords = Ext.Array.merge(mergedRecords, selectedRecords);
			i = Ext.Array.intersect(mergedRecords, valueStore.getRange()).length;
			if ((i != mergedRecords.length) || (i != me.valueStore.getCount())) {
				me.setValue(mergedRecords, false);
				if (!me.multiSelect || !me.pinList) {
					Ext.defer(me.collapse, 1, me);
				}
				if (valueStore.getCount() > 0) {
					me.fireEvent('select', me, valueStore.getRange());
				}
			}
			me.inputEl.focus();
			if (!me.pinList) {
				me.inputEl.dom.value = '';
			}
			if (me.selectOnFocus) {
				me.inputEl.dom.select();
			}
		}
	},
	syncSelection: function () {
		var me = this,
			picker = me.picker,
			valueField = me.valueField,
			pickStore, selection, selModel;
		if (picker) {
			pickStore = picker.store;
			selection = [];
			if (me.valueStore) {
				me.valueStore.each(function (rec) {
					var i = pickStore.findExact(valueField, rec.get(valueField));
					if (i >= 0) {
						selection.push(pickStore.getAt(i));
					}
				});
			}
			me.ignoreSelection++;
			selModel = picker.getSelectionModel();
			selModel.deselectAll();
			if (selection.length > 0) {
				selModel.select(selection);
			}
			if (me.ignoreSelection > 0) {
				--me.ignoreSelection;
			}
		}
	},
	doAlign: function () {
		var me = this,
			picker = me.picker,
			aboveSfx = '-above',
			isAbove;
		me.picker.alignTo(me.listWrapper, me.pickerAlign, me.pickerOffset);
		isAbove = picker.el.getY() < me.inputEl.getY();
		me.bodyEl[isAbove ? 'addCls' : 'removeCls'](me.openCls + aboveSfx);
		picker[isAbove ? 'addCls' : 'removeCls'](picker.baseCls + aboveSfx);
	},
	alignPicker: function () {
		var me = this,
			picker = me.picker,
			pickerScrollPos = picker.getTargetEl().dom.scrollTop;
		me.callParent(arguments);
		if (me.isExpanded) {
			if (me.matchFieldWidth) {
				// Auto the height (it will be constrained by min and max width) unless there are no records to display.
				picker.setWidth(me.listWrapper.getWidth());
			}
			picker.getTargetEl().dom.scrollTop = pickerScrollPos;
		}
	},
	getCursorPosition: function () {
		var cursorPos;
		if (Ext.isIE) {
			cursorPos = document.selection.createRange();
			cursorPos.collapse(true);
			cursorPos.moveStart("character", -this.inputEl.dom.value.length);
			cursorPos = cursorPos.text.length;
		} else {
			cursorPos = this.inputEl.dom.selectionStart;
		}
		return cursorPos;
	},
	hasSelectedText: function () {
		var sel, range;
		if (Ext.isIE) {
			sel = document.selection;
			range = sel.createRange();
			return (range.parentElement() == this.inputEl.dom);
		} else {
			return this.inputEl.dom.selectionStart != this.inputEl.dom.selectionEnd;
		}
	},
	onKeyDown: function (e, t) {
		var me = this,
			key = e.getKey(),
			rawValue = me.inputEl.dom.value,
			valueStore = me.valueStore,
			selModel = me.selectionModel,
			stopEvent = false;
		if (me.readOnly || me.disabled || !me.editable) {
			return;
		}
		if (me.isExpanded && (key == e.A && e.ctrlKey)) {
			// CTRL-A when picker is expanded - add all items in current picker store page to current value
			me.select(me.getStore().getRange());
			selModel.setLastFocused(null);
			selModel.deselectAll();
			me.collapse();
			me.inputEl.focus();
			stopEvent = true;
		} else if ((valueStore.getCount() > 0) && ((rawValue == '') || ((me.getCursorPosition() === 0) && !me.hasSelectedText()))) {
			// Keyboard navigation of current values
			var lastSelectionIndex = (selModel.getCount() > 0) ? valueStore.indexOf(selModel.getLastSelected() || selModel.getLastFocused()) : -1;
			if ((key == e.BACKSPACE) || (key == e.DELETE)) {
				if (lastSelectionIndex > -1) {
					if (selModel.getCount() > 1) {
						lastSelectionIndex = -1;
					}
					me.valueStore.remove(selModel.getSelection());
				} else {
					me.valueStore.remove(me.valueStore.last());
				}
				selModel.clearSelections();
				me.setValue(me.valueStore.getRange());
				if (lastSelectionIndex > 0) {
					selModel.select(lastSelectionIndex - 1);
				}
				stopEvent = true;
			} else if ((key == e.RIGHT) || (key == e.LEFT)) {
				if ((lastSelectionIndex == -1) && (key == e.LEFT)) {
					selModel.select(valueStore.last());
					stopEvent = true;
				} else if (lastSelectionIndex > -1) {
					if (key == e.RIGHT) {
						if (lastSelectionIndex < (valueStore.getCount() - 1)) {
							selModel.select(lastSelectionIndex + 1, e.shiftKey);
							stopEvent = true;
						} else if (!e.shiftKey) {
							selModel.setLastFocused(null);
							selModel.deselectAll();
							stopEvent = true;
						}
					} else if ((key == e.LEFT) && (lastSelectionIndex > 0)) {
						selModel.select(lastSelectionIndex - 1, e.shiftKey);
						stopEvent = true;
					}
				}
			} else if (key == e.A && e.ctrlKey) {
				selModel.selectAll();
				stopEvent = e.A;
			}
			me.inputEl.focus();
		}
		if (stopEvent) {
			me.preventKeyUpEvent = stopEvent;
			e.stopEvent();
			return;
		}
		// Prevent key up processing for enter if it is being handled by the picker
		if (me.isExpanded && (key == e.ENTER) && me.picker.highlightedItem) {
			me.preventKeyUpEvent = true;
		}
		if (me.enableKeyEvents) {
			me.callParent(arguments);
		}
		if (!e.isSpecialKey() && !e.hasModifier()) {
			me.selectionModel.setLastFocused(null);
			me.selectionModel.deselectAll();
			me.inputEl.focus();
		}
	},
	onKeyUp: function (e, t) {
		var me = this,
			rawValue = me.inputEl.dom.value;
		if (me.preventKeyUpEvent) {
			e.stopEvent();
			if ((me.preventKeyUpEvent === true) || (e.getKey() === me.preventKeyUpEvent)) {
				delete me.preventKeyUpEvent;
			}
			return;
		}
		if (me.multiSelect && (me.delimiterRegexp && me.delimiterRegexp.test(rawValue)) || ((me.createNewOnEnter === true) && e.getKey() == e.ENTER)) {
			rawValue = Ext.Array.clean(rawValue.split(me.delimiterRegexp));
			me.inputEl.dom.value = '';
			me.setValue(me.valueStore.getRange().concat(rawValue));
			me.inputEl.focus();
		}
		me.callParent([e, t]);
	},
	onPaste: function (e, t) {
		var me = this,
			rawValue = me.inputEl.dom.value,
			clipboard = (e && e.browserEvent && e.browserEvent.clipboardData) ? e.browserEvent.clipboardData : false;
		if (me.multiSelect && (me.delimiterRegexp && me.delimiterRegexp.test(rawValue))) {
			if (clipboard && clipboard.getData) {
				if (/text\/plain/.test(clipboard.types)) {
					rawValue = clipboard.getData('text/plain');
				} else if (/text\/html/.test(clipboard.types)) {
					rawValue = clipboard.getData('text/html');
				}
			}
			rawValue = Ext.Array.clean(rawValue.split(me.delimiterRegexp));
			me.inputEl.dom.value = '';
			me.setValue(me.valueStore.getRange().concat(rawValue));
			me.inputEl.focus();
		}
	},
	onExpand: function () {
		var me = this,
			keyNav = me.listKeyNav;
		me.callParent(arguments);
		if (keyNav || !me.filterPickList) {
			return;
		}
		keyNav = me.listKeyNav;
		keyNav.highlightAt = function (index) {
			var boundList = this.boundList,
				item = boundList.all.item(index),
				len = boundList.all.getCount(),
				direction;
			if (item && item.hasCls('x-boundlist-selected')) {
				if ((index == 0) || !boundList.highlightedItem || (boundList.indexOf(boundList.highlightedItem) < index)) {
					direction = 1;
				} else {
					direction = -1;
				}
				do {
					index = index + direction;
					item = boundList.all.item(index);
				} while ((index > 0) && (index < len) && item.hasCls('x-boundlist-selected'));
				if (item.hasCls('x-boundlist-selected')) {
					return;
				}
			}
			if (item) {
				item = item.dom;
				boundList.highlightItem(item);
				boundList.getTargetEl().scrollChildIntoView(item, false);
			}
		};
	},
	onTypeAhead: function () {
		var me = this,
			displayField = me.displayField,
			inputElDom = me.inputEl.dom,
			valueStore = me.valueStore,
			boundList = me.getPicker(),
			record, newValue, len, selStart;
		if (me.filterPickList) {
			var fn = this.createFilterFn(displayField, inputElDom.value);
			record = me.store.findBy(function (rec) {
				return ((valueStore.indexOfId(rec.getId()) === -1) && fn(rec));
			});
			record = (record === -1) ? false : me.store.getAt(record);
		} else {
			record = me.store.findRecord(displayField, inputElDom.value);
		}
		if (record) {
			newValue = record.get(displayField);
			len = newValue.length;
			selStart = inputElDom.value.length;
			boundList.highlightItem(boundList.getNode(record));
			if (selStart !== 0 && selStart !== len) {
				inputElDom.value = newValue;
				me.selectText(selStart, newValue.length);
			}
		}
	},
	onItemListClick: function (evt, el, o) {
		var me = this,
			itemEl = evt.getTarget('.deppon-boxselect-item'),
			closeEl = itemEl ? evt.getTarget('.deppon-boxselect-item-close') : false;
		if (me.readOnly || me.disabled) {
			return;
		}
		evt.stopPropagation();
		if (itemEl) {
			if (closeEl) {
				me.removeByListItemNode(itemEl);
				if (me.valueStore.getCount() > 0) {
					me.fireEvent('select', me, me.valueStore.getRange());
				}
			} else {
				me.toggleSelectionByListItemNode(itemEl, evt.shiftKey);
			}
			me.inputEl.focus();
		} else {
			if (me.selectionModel.getCount() > 0) {
				me.selectionModel.setLastFocused(null);
				me.selectionModel.deselectAll();
			}
			if (me.triggerOnClick) {
				me.onTriggerClick();
			}
		}
	},
	getMultiSelectItemMarkup: function () {
		var me = this;
		if (!me.multiSelectItemTpl) {
			if (!me.labelTpl) {
				me.labelTpl = Ext.create('Ext.XTemplate', '{[values.' + me.displayField + ']}');
			} else if (Ext.isString(me.labelTpl) || Ext.isArray(me.labelTpl)) {
				me.labelTpl = Ext.create('Ext.XTemplate', me.labelTpl);
			}
			me.multiSelectItemTpl = ['<tpl for=".">', '<li class="deppon-boxselect-item ', '<tpl if="this.isSelected(values.' + me.valueField + ')">', ' selected', '</tpl>', '" qtip="{[typeof values === "string" ? values : values.' + me.displayField + ']}">', '<div class="deppon-boxselect-item-text">{[typeof values === "string" ? values : this.getItemLabel(values)]}</div>', '<div class="deppon-tab-close-btn  deppon-boxselect-item-close"></div>', '</li>', '</tpl>', {
				compile: true,
				disableFormats: true,
				isSelected: function (value) {
					var i = me.valueStore.findExact(me.valueField, value);
					if (i >= 0) {
						return me.selectionModel.isSelected(me.valueStore.getAt(i));
					}
					return false;
				},
				getItemLabel: function (values) {
					return me.getTpl('labelTpl').apply(values);
				}
			}];
		}
		return this.getTpl('multiSelectItemTpl').apply(Ext.Array.pluck(this.valueStore.getRange(), 'data'));
	},
	applyMultiselectItemMarkup: function () {
		var me = this,
			itemList = me.itemList,
			item;
		if (itemList) {
			while ((item = me.inputElCt.prev()) != null) {
				item.remove();
			}
			me.inputElCt.insertHtml('beforeBegin', me.getMultiSelectItemMarkup());
		}
		Ext.Function.defer(function () {
			if (me.picker && me.isExpanded) {
				me.alignPicker();
			}
			if (me.hasFocus) {
				me.inputElCt.scrollIntoView(me.listWrapper);
			}
		}, 15);
	},
	getRecordByListItemNode: function (itemEl) {
		var me = this,
			itemIdx = 0,
			searchEl = me.itemList.dom.firstChild;
		while (searchEl && searchEl.nextSibling) {
			if (searchEl == itemEl) {
				break;
			}
			itemIdx++;
			searchEl = searchEl.nextSibling;
		}
		itemIdx = (searchEl == itemEl) ? itemIdx : false;
		if (itemIdx === false) {
			return false;
		}
		return me.valueStore.getAt(itemIdx);
	},
	toggleSelectionByListItemNode: function (itemEl, keepExisting) {
		var me = this,
			rec = me.getRecordByListItemNode(itemEl),
			selModel = me.selectionModel;
		if (rec) {
			if (selModel.isSelected(rec)) {
				if (selModel.isFocused(rec)) {
					selModel.setLastFocused(null);
				}
				selModel.deselect(rec);
			} else {
				selModel.select(rec, keepExisting);
			}
		}
	},
	removeByListItemNode: function (itemEl) {
		var me = this,
			rec = me.getRecordByListItemNode(itemEl);
		if (rec) {
			me.valueStore.remove(rec);
			me.setValue(me.valueStore.getRange());
		}
	},
	getRawValue: function () {
		var me = this,
			inputEl = me.inputEl,
			result;
		me.inputEl = false;
		result = me.callParent(arguments);
		me.inputEl = inputEl;
		return result;
	},
	setRawValue: function (value) {
		var me = this,
			inputEl = me.inputEl,
			result;
		me.inputEl = false;
		result = me.callParent([value]);
		me.inputEl = inputEl;
		return result;
	},
	addValue: function (value) {
		var me = this;
		if (value) {
			me.setValue(Ext.Array.merge(me.value, Ext.Array.from(value)));
		}
	},
	removeValue: function (value) {
		var me = this;
		if (value) {
			me.setValue(Ext.Array.difference(me.value, Ext.Array.from(value)));
		}
	},
	setValue: function (value, doSelect, skipLoad) {
		var me = this,
			valueStore = me.valueStore,
			valueField = me.valueField,
			record, len, i, valueRecord, h,
			unknownValues = [];
		if (Ext.isEmpty(value)) {
			value = null;
		}
		if (Ext.isString(value) && me.multiSelect) {
			value = value.split(me.delimiter);
		}
		value = Ext.Array.from(value, true);
		for (i = 0, len = value.length; i < len; i++) {
			record = value[i];
			if (!record || !record.isModel) {
				valueRecord = valueStore.findExact(valueField, record);
				if (valueRecord >= 0) {
					value[i] = valueStore.getAt(valueRecord);
				} else {
					valueRecord = me.findRecord(valueField, record);
					if (!valueRecord) {
						if (me.forceSelection) {
							unknownValues.push(record);
						} else {
							valueRecord = {};
							valueRecord[me.valueField] = record;
							valueRecord[me.displayField] = record;
							valueRecord = new me.valueStore.model(valueRecord);
						}
					}
					if (valueRecord) {
						value[i] = valueRecord;
					}
				}
			}
		}
		if ((skipLoad !== true) && (unknownValues.length > 0) && (me.queryMode === 'remote')) {
			var params = {};
			params[me.valueField] = unknownValues.join(me.delimiter);
			me.store.loadPage(1, {
				params: params,
				callback: function () {
					if (me.itemList) {
						me.itemList.unmask();
					}
					me.setValue(value, doSelect, true);
					me.autoSize();
				}
			});
			return false;
		}
		// For single-select boxes, use the last good (formal record) value if possible
		if (!me.multiSelect && (value.length > 0)) {
			for (i = value.length - 1; i >= 0; i--) {
				if (value[i].isModel) {
					value = value[i];
					break;
				}
			}
			if (Ext.isArray(value)) {
				value = value[value.length - 1];
			}
		}
		return me.callParent([value, doSelect]);
	},
	getValueRecords: function () {
		return this.valueStore.getRange();
	},
	getSubmitData: function () {
		var me = this,
			val = me.callParent(arguments);
		if (me.multiSelect && me.encodeSubmitValue && val && val[me.name]) {
			val[me.name] = Ext.encode(val[me.name]);
		}
		return val;
	},
	mimicBlur: function () {
		var me = this;
		if (me.selectOnTab && me.picker && me.picker.highlightedItem) {
			me.inputEl.dom.value = '';
		}
		me.callParent(arguments);
	},
	assertValue: function () {
		var me = this,
			rawValue = me.inputEl.dom.value,
			rec = !Ext.isEmpty(rawValue) ? me.findRecordByDisplay(rawValue) : false,
			value = false;
		if (!rec && !me.forceSelection && me.createNewOnBlur && !Ext.isEmpty(rawValue)) {
			value = rawValue;
		} else if (rec) {
			value = rec;
		}
		if (value) {
			me.addValue(value);
		}
		me.inputEl.dom.value = '';
		me.collapse();
	},
	checkChange: function () {
		if (!this.suspendCheckChange && !this.isDestroyed) {
			var me = this,
				valueStore = me.valueStore,
				lastValue = me.lastValue,
				valueField = me.valueField,
				newValue = Ext.Array.map(Ext.Array.from(me.value), function (val) {
					if (val.isModel) {
						return val.get(valueField);
					}
					return val;
				}, this).join(this.delimiter),
				isEqual = me.isEqual(newValue, lastValue);
			if (!isEqual || ((newValue.length > 0 && valueStore.getCount() < newValue.length))) {
				valueStore.suspendEvents();
				valueStore.removeAll();
				if (Ext.isArray(me.valueModels)) {
					valueStore.add(me.valueModels);
				}
				valueStore.resumeEvents();
				valueStore.fireEvent('datachanged', valueStore);
				if (!isEqual) {
					me.lastValue = newValue;
					me.fireEvent('change', me, newValue, lastValue);
					me.onChange(newValue, lastValue);
				}
			}
		}
	},
	isEqual: function (v1, v2) {
		var fromArray = Ext.Array.from,
			valueField = this.valueField,
			i, len, t1, t2;
		v1 = fromArray(v1);
		v2 = fromArray(v2);
		len = v1.length;
		if (len !== v2.length) {
			return false;
		}
		for (i = 0; i < len; i++) {
			t1 = v1[i].isModel ? v1[i].get(valueField) : v1[i];
			t2 = v2[i].isModel ? v2[i].get(valueField) : v2[i];
			if (t1 !== t2) {
				return false;
			}
		}
		return true;
	},
	applyEmptyText: function () {
		var me = this,
			emptyText = me.emptyText,
			inputEl, isEmpty;
		if (me.rendered && emptyText) {
			isEmpty = Ext.isEmpty(me.value) && !me.hasFocus;
			inputEl = me.inputEl;
			if (isEmpty) {
				inputEl.dom.value = emptyText;
				inputEl.addCls(me.emptyCls);
				me.listWrapper.addCls(me.emptyCls);
			} else {
				if (inputEl.dom.value === emptyText) {
					inputEl.dom.value = '';
				}
				me.listWrapper.removeCls(me.emptyCls);
				inputEl.removeCls(me.emptyCls);
			}
			me.autoSize();
		}
	},
	preFocus: function () {
		var me = this,
			inputEl = me.inputEl,
			emptyText = me.emptyText,
			isEmpty;
		if (emptyText && inputEl.dom.value === emptyText) {
			inputEl.dom.value = '';
			isEmpty = true;
			inputEl.removeCls(me.emptyCls);
			me.listWrapper.removeCls(me.emptyCls);
		}
		if (me.selectOnFocus || isEmpty) {
			inputEl.dom.select();
		}
	},
	onFocus: function () {
		var me = this,
			focusCls = me.focusCls,
			itemList = me.itemList;
		if (focusCls && itemList) {
			itemList.addCls(focusCls);
		}
		me.callParent(arguments);
	},
	onBlur: function () {
		var me = this,
			focusCls = me.focusCls,
			itemList = me.itemList;
		if (focusCls && itemList) {
			itemList.removeCls(focusCls);
		}
		me.callParent(arguments);
	},
	renderActiveError: function () {
		var me = this,
			invalidCls = me.invalidCls,
			itemList = me.itemList,
			hasError = me.hasActiveError();
		if (invalidCls && itemList) {
			itemList[hasError ? 'addCls' : 'removeCls'](me.invalidCls + '-field');
		}
		me.callParent(arguments);
	},
	autoSize: function () {
		var me = this,
			height;
		if (me.grow && me.rendered) {
			me.autoSizing = true;
			me.updateLayout();
		}
		return me;
	},
	afterComponentLayout: function () {
		var me = this,
			width;
		if (me.autoSizing) {
			height = me.getHeight();
			if (height !== me.lastInputHeight) {
				if (me.isExpanded) {
					me.alignPicker();
				}
				me.fireEvent('autosize', me, height);
				me.lastInputHeight = height;
				delete me.autoSizing;
			}
		}
	}
});

Ext.define('Ext.ux.layout.component.field.BoxSelectField', {
	alias: ['layout.boxselectfield'],
	extend: 'Ext.layout.component.field.Trigger',
	type: 'boxselectfield',
	waitForOuterWidthInDom: true,
	beginLayout: function (ownerContext) {
		var me = this,
			owner = me.owner;
		me.callParent(arguments);
		ownerContext.inputElCtContext = ownerContext.getEl('inputElCt');
		owner.inputElCt.setStyle('width', '');
		me.skipInputGrowth = !owner.grow || !owner.multiSelect;
	},
	beginLayoutFixed: function (ownerContext, width, suffix) {
		var me = this,
			owner = ownerContext.target;
		owner.triggerEl.setStyle('height', '24px');
		me.callParent(arguments);
		if (ownerContext.heightModel.fixed && ownerContext.lastBox) {
			owner.listWrapper.setStyle('height', ownerContext.lastBox.height + 'px');
			owner.itemList.setStyle('height', '100%');
		}
	},
	publishInnerWidth: function (ownerContext) {
		var me = this,
			owner = me.owner,
			width = owner.itemList.getWidth(true) - 10,
			lastEntry = owner.inputElCt.prev(null, true);
		if (lastEntry && !owner.stacked) {
			lastEntry = Ext.fly(lastEntry);
			width = width - lastEntry.getOffsetsTo(lastEntry.up(''))[0] - lastEntry.getWidth();
		}
		if (!me.skipInputGrowth && (width < 35)) {
			width = width - 10;
		} else if (width < 1) {
			width = 1;
		}
		ownerContext.inputElCtContext.setWidth(width);
	}
});

//读取文件MultipleSelector.js
/**
 *	@docauthor 曾宪涛 <rxjh2ht@163.com>
 *
 *	多选选择器，公共选择器之一。
 *
 */
Ext.define('Deppon.selector.MultipleSelector', {
	extend: 'Ext.ux.form.field.BoxSelect',
	alias: ['widget.multipleselector','widget.dynamicmulticomboselector'],
	alternateClassName: ['Deppon.commonselector.DynamicMultiSelectComboSelector'],
	/**
     * @cfg {String} triggerCls
     * 触发按钮样式。
     */
	triggerCls: Ext.baseCSSPrefix + 'form-search-trigger',
	/**
     * @cfg {Number} listWidth
     * 下拉列表宽度。
     */
	listWidth: this.width,
	/**
     * @cfg {Boolean} isPaging
     * 是否支持分页。
     */
	isPaging: false,
	/**
     * @cfg {Boolean} isEnterQuery
     * 是否支持回车查询。
     */
	isEnterQuery: false,
	/**
     * @cfg {Boolean} triggerOnClick
     * 点击获取焦点是，是否触发触发按钮。
     */
	triggerOnClick: false,
	/**
     * @cfg {Number} minChars
     * 查询条件显示结果的最小字符数。
     */
	minChars: 1,
	/**
     * @cfg {Object} listConfig
     * 下拉列表的配置对象。
	 *   - {@link Ext.view.BoundList#cls} - 默认为空。
     *   - {@link Ext.view.BoundList#emptyText} - 默认为空字符串。
     *   - {@link Ext.view.BoundList#itemSelector} - 默认为BoundList定义的字符串。
     *   - {@link Ext.view.BoundList#loadingText} - 默认为 "Loading..."
     *   - {@link Ext.view.BoundList#minWidth} - 默认为 70
     *   - {@link Ext.view.BoundList#maxWidth} - 默认为 `undefined`
     *   - {@link Ext.view.BoundList#maxHeight} - 默认为 `300`
     *   - {@link Ext.view.BoundList#resizable} - 默认为 `false`
     *   - {@link Ext.view.BoundList#shadow} - 默认为 `'sides'`
     *   - {@link Ext.view.BoundList#width} - 默认为 `undefined` （自动匹配宽度，如果combobox的{@link #matchFieldWidth}属性设置为true）
     */
	listConfig: {
		getInnerTpl: function () {
			return this.up('combo').showContent;
		}
	},
	/**
	 * @private
     * combobox的keypress监听事件。
     */
	/*onKeyPress: function (e, t) {
		var me = this;
		if (e.getKey() == e.ENTER) {
			me.doRawQuery();
		} else {
			me.collapse();
		}
	},*/
	onSpecialkey: function (field, e, eOpts) {
		var me = this,
			key = e.getKey();
		if (key == e.ENTER) {
			me.doRawQuery();
		}
	},
	/**
	 * @protected
     * 初始化监听事件方法。
     */
	initEvents: function () {
		var me = this;
		me.callParent(arguments);
		if (me.enableKeyEvents && me.isEnterQuery) {
			//me.mon(me.inputEl, 'keypress', me.onKeyPress, me);
			me.mon(me, 'specialkey', me.onSpecialkey, me);
		}
	},
	/**
	 * @private
     * 组件初始化方法。
     */
	initComponent: function () {
		var me = this;
		me.on('expand', function (combo, eOpts) {
			combo.getPicker().minWidth = me.listWidth;
			combo.getPicker().setWidth(me.listWidth);
		});
		if (me.isPaging) {
			me.pageSize = me.store.pageSize;
		}
		if (me.isEnterQuery) {
			me.enableKeyEvents = true;
			me.queryDelay = 1000000;
		}
		if (me.showContent == null) {
			me.showContent = '{' + me.displayField + '}';
		}
		this.callParent(arguments);
	}
});
 

//读取文件LinkageContainer.js
/**
 *
 *	级联选择器，公共选择器之一。
 *
 */
 Ext.define('Deppon.selector.LinkageContainer', {
	extend: 'Ext.form.FieldContainer',
	alias: 'widget.linkagecontainer',
	alternateClassName: ['Deppon.commonselector.LinkageContainer'],
	
	/**
     * @cfg {String} triggerCls
     * 定义级联选择器内的选择器类型，默认类型是linkedselector。
     */
	defaultType: 'linkedselector',
	
	/**
	 * @method
     * 通过itemId得到级联选择器内的选择器的值。
	 * @param {String} itemId
	 * @return {Object} itemValue
     */
	getItemValue: function (itemId) {
		var me = this,
			item = me.items.get(itemId);
		if (item != null) {
			return item.getValue();
		}
		return null;
	},
	
	/**
	 * @method
     * 通过级联选择器内的所有选择器的值，并以数组的方式返回。
	 * @param {String} itemId
	 * @return {Array} itemValues
     */
	getValue: function () {
		var me = this,
			values = new Array();
		for (var i = 0; i < me.items.length; i++) {
			values[i] = me.getItemValue(me.items.items[i].itemId);
		}
		return values;
	}
});

//读取文件ButtonDisabledPlugin.js
/**
 *	@docauthor 曾宪涛 <rxjh2ht@163.com>
 *
 *	此插件使用于按钮上，使按钮在点击后一定时间内处于不可用状态（disabled），从而防止用户连续点击按钮。
 *
 *	通常可以用来防止用户连续提交而造成的服务器性能问题。
 *
 *	此插件的功能和设计参考12306订票查询按钮。
 *
 *	# 插件示例
 *	<pre><code>
 *	@example
 *	Ext.create('Ext.button.Button', {		
 *		frame: true,
 *		text: '阻止按钮连续点击插件',
 *		maxWidth: 200,
 *		height: 30,
 *		//插件配置代码
 *		plugins: {
 *			ptype: 'buttondisabledplugin',
 *			seconds: 5
 *		},
 *		renderTo : Ext.getBody()
 *	});
 *	</code></pre>
 */
Ext.define('Deppon.ux.ButtonDisabledPlugin', {
	alias: ['plugin.buttonlimitingplugin','plugin.buttondisabledplugin'],
	alternateClassName: 'Deppon.ux.ButtonLimitingPlugin',
	/**
     * @cfg {Number} seconds
     * 设置按钮不可用状态持续的时间（单位：秒）。
     */
	seconds: 2,
	/**
	 * @private
     * 构造函数，初始化此插件的配置项。
     */
	constructor: function(config) {
		Ext.apply(this, config);
	},
	/**
	 * @private
     * 插件初始化方法，适用于Button组件上。在组件的initComponent方法执行完毕后调用。
     */
	init: function(button) {
		var me = this;
		me.button = button;
		me.getDelayedTask();
		button.on('click', me.onClickButton, me);
	},
	/**
	 * @private
     * 按钮上的click监听事件，点击后按钮变成不可用状态，利用延迟任务组件，一定时间后恢复可用状态。
	 * @return {Boolean} 返回true，继续执行其他的click监听事件。
     */
	onClickButton: function() {
		var me = this,
			button = me.button;
		button.setDisabled(true);
		me.task.delay(me.seconds * 1000);
		return true;
	},
	/**
	 * @method
     * 设置按钮disabled状态。
	 * @param {Boolean} 传入false设置按钮可用，传入true设置按钮不可用。
     * @return {Ext.button.Button} 设置插件的按钮对象。
     */
	setButtonDisabled: function(disabled) {
		var me = this,
			button = me.button;
		disabled = disabled || false;
		button.setDisabled(disabled);
		return button;
	},
	/**
	 * @method
     * 获取插件的任务延迟对象。该方法在插件初始化时调用。
     * @return {Ext.util.DelayedTask} 插件的任务延迟（Ext.util.DelayedTask）对象。
     */
	getDelayedTask: function() {
		var me = this;
		if(!me.task) {
			me.task = new Ext.util.DelayedTask(me.setButtonDisabled, me);
		}
		return me.task;
	},
	/**
	 * @private
     * 销毁插件相关的属性和对象。该方法会在组件销毁的时候自动调用。
     */
	destory: function() {
		var me = this,
			button = me.button;
		Ext.destroy(me.task);
		delete me.button;
		delete me.task;
	}
});

//读取文件CheckColumn.js
/**
 * @class Deppon.grid.column.CheckColumn
 * @extends Ext.grid.column.Column
 * <p>在每个列的单元格中显示一个复选框，单击复选框，可实现与单元格绑定的相关数据的字段值改变.</p>
 * <p>用法：在需要设置grid中的某一列类型为xtype:'checkcolumn'</p>
 * <p>示例代码:</p>
 * <pre><code>
 * @example
 * Ext.create('Ext.data.Store', {
 * 	storeId:'simpsonsStore',
 * 	fields:['name', 'email', 'phone'],
 * 	data:{'items':[
 * 		{ 'name': 'Lisa',  "email":"lisa@simpsons.com",  "phone":"555-111-1224" ,"flag":false },
 * 		{ 'name': 'Bart',  "email":"bart@simpsons.com",  "phone":"555-222-1234" ,"flag":true },
 * 		{ 'name': 'Homer', "email":"home@simpsons.com",  "phone":"555-222-1244" ,"flag":false  },
 * 		{ 'name': 'Marge', "email":"marge@simpsons.com", "phone":"555-222-1254" ,"flag":true  }
 *	]},
 *	proxy: {
 *		type: 'memory',
 *		reader: {
 *			type: 'json',
 *			root: 'items'
 *		}
 *	}
 * });
 * Ext.create('Ext.grid.Panel', {
 * 	frame:true,
 * 	title: 'openwindowcolumn demo',
 * 	store: Ext.data.StoreManager.lookup('simpsonsStore'),
 *	columns: [
 *		{ header: 'Name',  dataIndex: 'name' },
 *		{ header: 'Email', dataIndex: 'email', flex: 1},
 *		{ header: 'Phone', dataIndex: 'phone' },
 *		{ header: 'Selected', dataIndex: 'flag' ,xtype: 'checkcolumn'}
 *	],
 *	height: 200,
 *	width: 400,
 *	renderTo:Ext.getBody()
 * });
 * </code></pre>
 */
Ext.define('Deppon.grid.column.CheckColumn', {
    extend: 'Ext.grid.column.Column',
    alias: 'widget.checkcolumn',

    /**
     * @cfg {Boolean} [stopSelection=true]
     * 单击的时候，阻止表格行选择
     */
    stopSelection: true,

    tdCls: Ext.baseCSSPrefix + 'grid-cell-checkcolumn',

    constructor: function() {
        this.addEvents(
            /**
             * @event beforecheckchange
             * 当复选框的状态改变之前触发。
             * @param {Deppon.grid.column.CheckColumn} this CheckColumn
             * @param {Number} rowIndex 当前复选框所在行的行号
             * @param {Boolean} checked 复选框是否选择
             */
            'beforecheckchange',
            /**
             * @event checkchange
             * 当复选框的状态改变时触发。
             * @param {Deppon.grid.column.CheckColumn} this CheckColumn
             * @param {Number} rowIndex 当前复选框所在行的行号
             * @param {Boolean} checked 复选框是否选择
             */
            'checkchange'
        );
        this.callParent(arguments);
    },

    /**
     * @private
     * 绑定事件到GridView的事件中
     */
    processEvent: function(type, view, cell, recordIndex, cellIndex, e) {
        var me = this,
            key = type === 'keydown' && e.getKey(),
            mousedown = type == 'mousedown';

        if (mousedown || (key == e.ENTER || key == e.SPACE)) {
            var record = view.panel.store.getAt(recordIndex),
                dataIndex = me.dataIndex,
                checked = !record.get(dataIndex);

            // Allow apps to hook beforecheckchange
            if (me.fireEvent('beforecheckchange', me, recordIndex, checked) !== false) {
                record.set(dataIndex, checked);
                me.fireEvent('checkchange', me, recordIndex, checked);

                // Mousedown on the now nonexistent cell causes the view to blur, so stop it continuing.
                if (mousedown) {
                    e.stopEvent();
                }

                // Selection will not proceed after this because of the DOM update caused by the record modification
                // Invoke the SelectionModel unless configured not to do so
                if (!me.stopSelection) {
                    view.selModel.selectByPosition({
                        row: recordIndex,
                        column: cellIndex
                    });
                }

                // Prevent the view from propagating the event to the selection model - we have done that job.
                return false;
            } else {
                // Prevent the view from propagating the event to the selection model if configured to do so.
                return !me.stopSelection;
            }
        } else {
            return me.callParent(arguments);
        }
    },

    // Note: class names are not placed on the prototype bc renderer scope
    // is not in the header.
    renderer : function(value){
        var cssPrefix = Ext.baseCSSPrefix,
            cls = [cssPrefix + 'grid-checkheader'];

        if (value) {
            cls.push(cssPrefix + 'grid-checkheader-checked');
        }
        return '<div class="' + cls.join(' ') + '">&#160;</div>';
    }
});

//读取文件ClearValuePlugin.js
/**
 *	@docauthor 曾宪涛 <rxjh2ht@163.com>
 *
 *	此插件用于Combobox上，一键清除选择的内容。
 *
 *	# 插件示例
 *	<pre><code>
 *	@example
 *	Ext.create('Ext.form.field.ComboBox', {
 *		store: Ext.create('Ext.data.Store', {
 *			fields: ['abbr', 'name'],
 *			data: [
 *				{"abbr":"AL", "name":"Alabama"},
 *				{"abbr":"AK", "name":"Alaska"},
 *				{"abbr":"AZ", "name":"Arizona"}
 *			]
 *		}),
 *		fieldLabel: '清除combo内容插件',
 *		labelWidth: 120,
 *		queryMode: 'local',
 *		displayField: 'name',
 *		valueField: 'abbr',
 *		//插件配置代码
 *		plugins: {
 *			ptype: 'clearvalueplugin'
 *		},
 *		renderTo : Ext.getBody()
 *	});
 *	</code></pre>
 */
Ext.define('Deppon.ux.ClearValuePlugin', {
	alias: 'plugin.clearvalueplugin',
	/**
     * @cfg {String} trigger1Cls
     * 清除按钮的样式。
     */
	trigger1Cls: Ext.baseCSSPrefix + 'form-clear-trigger',
	/**
     * @cfg {String} trigger2Cls
     * 查询按钮的样式。
     */
    trigger2Cls: Ext.baseCSSPrefix + 'form-search-trigger',
	/**
	 * @private
     * 构造函数，初始化此插件的配置项。
     */
	constructor: function(config) {
		Ext.apply(this, config);
	},
	/**
	 * @private
     * 插件初始化方法，适用于combobox组件上。在组件的initComponent方法执行完毕后调用。
     */
	init: function(combo) {
		var me = this;
		me.pluginId='clearvalueplugin';
		combo.hasSearch = false;
		combo.trigger1Cls = me.trigger1Cls;
		combo.trigger2Cls = me.trigger2Cls;
		combo.onTrigger1Click = me.onTrigger1Click;
		combo.onTrigger2Click = me.onTrigger2Click;
		combo.on('afterrender', me.afterRender, combo);
	},
	/**
	 * @private
     * combobox组件afterrender监听事件，用于使清除按钮在渲染后处于隐藏状态。
     */
    afterRender: function() {
        this.triggerCell.item(0).setDisplayed(false);
    },
	/**
	 * @protected
     * 清除按钮的点击事件。
     */
	onTrigger1Click: function() {
        var me = this;
        if (me.hasSearch) {
            me.setValue('');
            me.hasSearch = false;
            me.triggerCell.item(0).setDisplayed(false);
            me.updateLayout();
        }
    },
	/**
	 * @protected
     * 查询按钮的点击事件。
     */
    onTrigger2Click: function() {
    	var me = this;
    	me.onTriggerClick();
    	me.hasSearch = true;
    	me.triggerCell.item(0).setDisplayed(true);
    }
});

//读取文件ComboButton.js
/**
 * @class Deppon.button.ComboButton
 * @extends Ext.container.Container
 * <p>实现在按钮的右边提供一个下拉菜单</p>
 * <p><u>代码示例</u></p>
 * <pre><code>
 * @example
 * Ext.onReady(function(){
 *  Ext.create('Deppon.button.ComboButton', {   
 *      text: '按钮文字',
 *      //定义按钮的点击事件
 *      handler:function(){}, 
 *      tabIndex :1,
 *      textAlign:'center',
 *      //定义按钮点击后出现的菜单
 *      menu:[{
 *          text:'b1'
 *      },{
 *          text:'b2'
 *      },{
 *          text:'b3'
 *      }],
 *      renderTo:Ext.getBody()
 *  });
 * });
 * </pre></code>
 */
Ext.define('Deppon.button.ComboButton', {
	extend: 'Ext.container.Container',
	alias: 'widget.comboButton',
	layout: 'hbox',
	
	/**
	 * @cfg {String} text
	 * text 设置按钮上的文本信息
	 */
	
	/**
	 * @cfg {object} 	textAlign
	 * textAlign 设置按钮的文本显示位置
	 */
	
	/**
	 * @cfg {number} 	tabIndex
	 * tabIndex 设置键盘tab键的顺序
	 */
	
	/**
	 * @cfg {Function} handler
	 * 定义按钮的点击回调方法
	 * @param {Ext.button.Button} handler.button 当前按钮.
	 * @param {Ext.EventObject} handler.e 点击事件.
	 */
	
	/**
	 * @cfg {object}  menu
	 * menu 定义按钮点击后出现的菜单
	 * meun的定义如下:
	 *		menu:[{
	 *			text:'b1'
	 *		},{
	 *			text:'b2'
	 *		},{
	 *			text:'b3'
	 *		}]
	 */
	
	/**
	 * @private
	 * @cfg {Ext.button.Button} arrow
	 * 箭头下拉按钮。
	 */
	arrow: null,
	
	/**
	 * @private
	 * @cfg {Ext.button.Button} button
	 * 点击按钮。
	 */
	button: null,
	
	/**
	 * @private
	 * 获得菜单的宽度，
	 */
	menuWidth: function(menuCnfg, padding, text, buttonWidth) {
		if(menuCnfg.width) {
			return menuCnfg.width;
		} else if(buttonWidth) {
			return buttonWidth;
		} else if(padding) {
			padding=padding.replace(/[A-Za-z]+/g, '');
			var s = padding.split(' ');
			return (text.length*7) + parseInt(s[1]) + parseInt(s[3]) + 14;
		} else {
			return (text.length*7)+24;
		}
	},
	
	/**
	 * @private
	 * 创建下拉菜单的箭头按钮
	 */
	createArrow: function(cnfg) {
		if (!Ext.isEmpty(this.arrow)) {
			return false;
		} else {
			var c_menu = {};
			c_menu.items = cnfg.menu;
			c_menu.maxWidth = this.menuWidth(cnfg.menu, cnfg.padding, cnfg.text, cnfg.width);
			this.arrow = Ext.create('Ext.Button',{
				cls: cnfg.cls,
				menuAlign: 'tr-br',
				padding: '0 0 0 0',
				margin: '0 0 0 0',
				width: 5,
				menu: c_menu
			});
			return true;
		}
	},
	
	/**
	 * @private
	 * 创建按钮
	 */
	createButton: function(cnfg) {
		if (this.button) {
			return this.button;
		} else {
			if(cnfg.width) {
				cnfg.width = cnfg.width - 14;
			}
			cnfg.margin='0 0 0 0';
			this.button=Ext.create('Ext.Button',cnfg);;
			return true;
		}
	},
	
	/**
	 * @private
	 * 组件初始化方法。
	 * 
	 * 主要执行初始化Items、添加监听事件、绑定Store等操作。
	 */
	constructor:function(cnfg) {
		var c_cnfg=cnfg;
		delete c_cnfg.margin;
		this.config = this.config || {};
		Ext.apply(this, this.config);
		c_cnfg.menuAlign='tl-tl';
		this.createArrow(c_cnfg);
		this.arrow.addClass('dp-arrow-button');
		this.arrow.menu.addClass('dp-comboButton-menu');
		delete c_cnfg.menu;
		this.createButton(c_cnfg);
		delete cnfg.padding;
		this.items=[this.button, this.arrow];
		this.callParent(arguments);
		this.initConfig(cnfg);
	}
});

//读取文件DateTimeField.js
/**
 *	@docauthor 曾宪涛 <rxjh2ht@163.com>
 *
 *	此组件封装My97DatePicker日期时间组件，实现基于Extjs框架的日期时间组件。
 *
 *	My97DatePicker日期时间组件是国内一款兼容各类浏览器的纯js开发的日期时间组件。
 *
 *	# 组件示例
 *	<pre><code>
 *	@example
 *	Ext.create('Ext.form.Panel', {
 *		renderTo: Ext.getBody(),
 *		width: 300,
 *		bodyPadding: 10,
 *		title: '示例',
 *		items: [{
 *			fieldLabel: '日期时间组件',
 *			xtype: 'my97datetimefield',
 *			anchor: '100%',
 *			name: 'date',
 *			id: 'my97datetime',
 *			dateConfig: {
 *				//组件显示所必须的配置，为组件id + ‘-inputEl’。
 *				//因此单独使用my97datetimefield时需要手动设置组件id属性。
 *				el: 'my97datetime-inputEl',
 *				//设置最小选择日期。
 *				minDate: '2012-10-01',
 *				//设置最大选择日期。
 *				maxDate: '2013-10-01',
 *				//设置初始日期。
 *				startDate: '2013-06-01'
 *			}
 *		}]
 *	});
 *	</code></pre>
 */
Ext.define('Deppon.form.field.DateTimeField', {
	extend: 'Ext.form.field.Trigger',
	alias: ['widget.my97datetimefield','widget.datetimefield_date97'],
    /**
     * @cfg {Boolean} time
     * 是否提供时间选择功能。
     */
	time: true,
    /**
     * @cfg {Array} dateConfig
     * My97DatePicker组件配置。
     */
	dateConfig: [],
    /**
     * @cfg {String} initTime
     * 默认时间制为12小时制。
     */
	initTime: '12',
    /**
     * @cfg {String} initTimeFormat
     * 默认时间格式。
     */
    initTimeFormat: 'H',
    /**
     * @cfg {String} minText
     * 最小时间提示。
     */
	minText : "The date in this field must be equal to or after {0}",
    /**
     * @cfg {String} maxText
     * 最大时间提示。
     */
    maxText : "The date in this field must be equal to or before {0}",
    /**
     * @cfg {String} triggerCls
     * 组件trigger样式。
     */
	triggerCls: Ext.baseCSSPrefix + 'form-date-trigger',
	/**
	 * @private
     * 组件初始化方法。
     *
     * 主要执行初始化Items、添加监听事件、绑定Store等操作。
     */
	initComponent: function() {
		var me = this;
		me.callParent(arguments);
		this.initDateConfig();
	},
	/**
	 * @private
     * 组件显示的触发方法。
     */
	onTriggerClick: function(e) {
		var me = this;
		if (me.disabled || me.readOnly) {
			return;
		}
		me.onFocus({});
		var bodyWidth = document.body.clientWidth,
			xC = document.body.clientWidth - e.getX() - me.width,
			yC = document.body.clientHeight - e.getY() - me.height,
			x = 0, y = 0;
		if (xC > 0) {
			x = e.getX();
		} else {
			x = document.body.clientWidth - this.width - 4;
		}
		if (yC > 0) {
			y = e.getY();
		} else {
			y= document.body.clientHeight - this.height - 4;
		}
		WdatePicker(me.dateConfig);
	},
	/**
	 *	@method
     * 初始化My97DatePicker组件配置。
     * 初始化el、dateFmt、skin等属性。
     */
	initDateConfig: function() {
		var me = this;
		if(!me.dateConfig['el'])
			me.dateConfig['el'] = me.id;
		if(me.time) {
			me.addDateConfig("dateFmt", 'yyyy-MM-dd HH:mm:ss');
		} else if(!me.dateConfig["dateFmt"]) {
			me.dateConfig["dateFmt"] = 'yyyy-MM-dd';
		}
		if(!me.dateConfig["skin"]) {
			me.dateConfig["skin"] = 'ext';
		}
	},
	/**
	 *	@method
     *	设置My97DatePicker组件配置对象。
	 *	@param {Array} My97DatePicker组件配置对象。
     */
	setDateConfig: function(config) {
		this.dateConfig = config;
		this.initDateConfig();
	},
	/**
	 *	@method
     *	增加My97DatePicker组件配置属性。
	 *	@param {String} name 配置属性。
	 *	@param {String} value 配置属性对应的值。
     */
	addDateConfig: function(name, value) {
		this.removeDateConfig(name);
		this.dateConfig[name] = value;
	},
	/**
	 *	@method
     *	移除My97DatePicker组件配置属性。
	 *	@param {String} name 配置属性。
     */
	removeDateConfig: function(name) {
		var me = this,
			config = me.dateConfig,
			len = config.length;
		for (var i=0; i<len; i++) {
			var temp = config[i];
			if (temp && temp.split(':')[0] == name) {
				config.pop(i);
				return;
			}
		}
	},
	/**
     * 把字符串，根据指定的{@link Ext.Date#parse date格式}转换成Date对象。
     * @param {String} value 用来转换的值。
     * @param {String} format 有效的date格式 (参考 {@link Ext.Date#parse})
     * @return {Date} 转换后的Date对象，如果转换失败则返回null。
     */
	safeParse: function(value, format) {
        var me = this,
            utilDate = Ext.Date,
            result = null,
            strict = me.useStrict,
            parsedDate;
        if (utilDate.formatContainsHourInfo(format)) {
            result = utilDate.parse(value, format, strict);
        } else {
            parsedDate = utilDate.parse(value + ' ' + me.initTime, format + ' ' + me.initTimeFormat, strict);
            if (parsedDate) {
                result = utilDate.clearTime(parsedDate);
            }
        }
        return result;
    },
	/**
	 *	@method
     *	格式化date对象。
	 *	@param {String} value Date对象。
     *	@return {Date} val 格式化后的Date对象。
     */
	parseDate: function(value) {
        if(!value || Ext.isDate(value)){
            return value;
        }
        var me = this,
            val = me.safeParse(value, me.format),
            altFormats = me.altFormats,
            altFormatsArray = me.altFormatsArray,
            i = 0,
            len;
        if (!val && altFormats) {
            altFormatsArray = altFormatsArray || altFormats.split('|');
            len = altFormatsArray.length;
            for (; i < len && !val; ++i) {
                val = me.safeParse(value, altFormatsArray[i]);
            }
        }
        return val;
    },
	/**
	 *	@method
     *	格式化date对象。
	 *	@param {Date} date Date对象。
	 *	@param {String} format 格式。
     *	@return {String} 格式化后的date字符串。
     */
	formatDate: function(date, format) {
        return Ext.isDate(date) ? Ext.Date.dateFormat(date, format) : date;
    },
	/**
	 *	@method
     *	获取错误提示信息。
	 *	@param {String} value 组件的value。
     *	@return {String} errors 错误提示信息。
     */
	getErrors: function(value) {
		var me = this,
			errors = me.callParent(arguments),
			format = Ext.String.format,
			value, svalue, time,
            clearTime = Ext.Date.clearTime,
            minValue = me.dateConfig["minDate"],
            maxValue = me.dateConfig["maxDate"],
			cpaCmp, cpaCmpId, day;
		if (value === null || value.length < 1) {
             return errors;
        }
		var w = WdatePicker;
		me.format = me.dateConfig["dateFmt"];
		if(!Ext.isEmpty(me.format, true)) {
			if(me.format == 'yyyy-MM-dd HH:mm:ss')  { me.format = 'Y-m-d H:i:s'; }
			else if(me.format == 'yyyy-MM-dd') { me.format = 'Y-m-d'; }
			svalue = value;
			value = me.parseDate(value);
			if (!value) {
				errors.push(format(me.invalidText, svalue, Ext.Date.unescapeFormat(me.format)));
				return errors;
			}
		}
		if(minValue && maxValue) {
			time = value.getTime();
			if(minValue.length > maxValue.length) {
				cpaCmpId = maxValue.substring(maxValue.indexOf("'") + 1, maxValue.indexOf(")") - 1);
				day = minValue.substring(minValue.indexOf(":") + 1, minValue.indexOf(")") - 1);
				if(cpaCmpId && day) {
					cpaCmp = Ext.get(cpaCmpId).dom.value;
					if(cpaCmp) {
						maxValue = Ext.Date.parse(cpaCmp, me.format);
						minValue = Ext.Date.add(Ext.Date.parse(cpaCmp, me.format), Ext.Date.DAY, Number(day));
						if (minValue && time < minValue.getTime()) {
							errors.push(format(me.minText, me.formatDate(minValue, me.format)));
						}
						if (maxValue && time > maxValue.getTime()) {
							errors.push(format(me.maxText, me.formatDate(maxValue, me.format)));
						}
					}
				}
			} else {
				cpaCmpId = minValue.substring(minValue.indexOf("'") + 1, minValue.indexOf(")") - 1);
				day = maxValue.substring(maxValue.indexOf(":") + 1, maxValue.indexOf(")") - 1);
				if(cpaCmpId && day) {
					cpaCmp = Ext.get(cpaCmpId).dom.value;
					if(cpaCmp) {
						minValue = Ext.Date.parse(cpaCmp, me.format);
						maxValue = Ext.Date.add(Ext.Date.parse(cpaCmp, me.format), Ext.Date.DAY, Number(day));
						if (minValue && time < minValue.getTime()) {
							errors.push(format(me.minText, me.formatDate(minValue, me.format)));
						}
						if (maxValue && time > maxValue.getTime()) {
							errors.push(format(me.maxText, me.formatDate(maxValue, me.format)));
						}
					}
				}
			}
		}
		return errors;
	}
});

//读取文件EllipsisColumn.js
/**
 * @class Deppon.grid.column.Ellipsis
 * @extends Ext.grid.column.Column
 * <p>实现gird中列内容长度大列的默认宽度时的提示功能</p>
 * <p>用法:在grid中设置需要显示提示功能的列设置为xtype:'ellipsiscolumn'</p>
 * <p><u>代码示例:</u></p>
 * <pre><code>
 * @example
 * Ext.create('Ext.data.Store', {
 * 	storeId:'simpsonsStore',
 * 	fields:['name', 'email', 'phone'],
 * 	data:{'items':[
 *     	{ 'name': 'Lisa',  "email":"lisa@simpsons.com",  "phone":"555-111-1224" },
 *     	{ 'name': 'Bart',  "email":"bart@simpsons.com",  "phone":"555-222-1234"  },
 *     	{ 'name': 'Homer', "email":"home@simpsons.com",  "phone":"555-222-1244"  },
 *     	{ 'name': 'Marge', "email":"marge@simpsons.com", "phone":"555-222-1254"  }
 * 	]},
 * 	proxy: {
 *     	type: 'memory',
 *     	reader: {
 *         	type: 'json',
 *         	root: 'items'
 *     	}
 * 	}
 * });
 * Ext.create('Ext.grid.Panel', {
 * 	title: 'ellipsiscolumn demo',
 * 	frame: true,
 * 	store: Ext.data.StoreManager.lookup('simpsonsStore'),
 * 	columns: [
 *     	{ text: 'Name', dataIndex: 'name', xtype : 'ellipsiscolumn' },
 *     	{ text: 'Name', dataIndex: 'email', flex: 1, xtype : 'ellipsiscolumn'},
 *     	{ text: 'Name', dataIndex: 'phone', xtype : 'ellipsiscolumn' }
 * 	],
 * 	height: 200,
 * 	width: 400,
 * 	renderTo:Ext.getBody()
 * });
 * Ext.QuickTips.init();
 * </code></pre>
 */
Ext.define('Deppon.grid.column.Ellipsis', {
	extend: 'Ext.grid.column.Column',
	alias: 'widget.ellipsiscolumn',
	alternateClassName: ['Deppon.grid.EllipsisColumn'],
	initComponent: function() {
		var me = this,
			customerRenderer = me.renderer;
		if(customerRenderer) {
			me.renderer = function(value, metadata, record, rowIndex, columnIndex, store) {
				value = customerRenderer(value, metadata, record, rowIndex, columnIndex, store);
				value = me.defaultRenderer(value, metadata, record, rowIndex, columnIndex, store);
				return value;
			};
		}
		me.callParent(arguments);
	},
	
	defaultRenderer: function(value, metadata, record, rowIndex, columnIndex, store) {
		var me = this,
			metaValue = record.get(me.dataIndex);
		metadata.tdAttr = 'data-qtip="' + metaValue + '"';
		return value;
	}
});

//读取文件FileUploadGrid.js
﻿// ************************************** Uploader ************************************* //
// ************************************************** Ext.ux.Utils ************************************************** //
Ext.ns("Ext.ux.Utils");
Ext.ux.Utils.EventQueue = function(handler, scope) {
	if (!handler) {
		throw "Handler is required."
	}
	this.handler = handler;
	this.scope = scope || window;
	this.queue = [];
	this.is_processing = false;
	this.postEvent = function(event, data) {
		data = data || null;
		this.queue.push({
			event: event,
			data: data
		});
		if (!this.is_processing) {
			this.process();
		}
	};
	this.flushEventQueue = function() {
		this.queue = [];
	};
	this.process = function() {
		while (this.queue.length > 0) {
			this.is_processing = true;
			var event_data = this.queue.shift();
			this.handler.call(this.scope, event_data.event, event_data.data);
		}
		this.is_processing = false;
	}
};
Ext.ux.Utils.FSA = function(initial_state, trans_table, trans_table_scope) {
	this.current_state = initial_state;
	this.trans_table = trans_table || {};
	this.trans_table_scope = trans_table_scope || window;
	Ext.ux.Utils.FSA.superclass.constructor.call(this, this.processEvent, this);
};
Ext.extend(Ext.ux.Utils.FSA, Ext.ux.Utils.EventQueue, {	
	current_state: null,
	trans_table: null,
	trans_table_scope: null,
	state: function() {
		return this.current_state;
	},
	processEvent: function(event, data) {
		var transitions = this.currentStateEventTransitions(event);
		if (!transitions) {
			return;
			//throw "State '" + this.current_state + "' has no transition for event '" + event + "'.";
		}
		for (var i = 0, len = transitions.length; i < len; i++) {
			var transition = transitions[i],
				predicate = transition.predicate || transition.p || true,
				action = transition.action || transition.a || Ext.emptyFn,
				new_state = transition.state || transition.s || this.current_state,
				scope = transition.scope || this.trans_table_scope;
			if (this.computePredicate(predicate, scope, data, event)) {
				this.callAction(action, scope, data, event);
				this.current_state = new_state;
				return;
			}
		}
		//throw "State '" + this.current_state + "' has no transition for event '" + event + "' in current context";
	},
	currentStateEventTransitions: function(event) {
		return this.trans_table[this.current_state] ? this.trans_table[this.current_state][event] || false : false;
	},
	computePredicate: function(predicate, scope, data, event) {
		var result = false;
		switch (Ext.type(predicate)) {
			case "function" :
				result = predicate.call(scope, data, event, this);
				break;
			case "array" :
				result = true;
				for (var i = 0, len = predicate.length; result && (i < len); i++) {
					if (Ext.type(predicate[i]) == "function") {
						result = predicate[i].call(scope, data, event, this);
					} else {
						throw ["Predicate: ", predicate[i], ' is not callable in "', this.current_state, '" state for event "', event].join("");
					}
				}
				break;
			case "boolean" :
				result = predicate;
				break;
			default :
				throw ["Predicate: ", predicate, ' is not callable in "', this.current_state, '" state for event "', event].join("");
		}
		return result;
	},
	callAction: function(action, scope, data, event) {
		switch (Ext.type(action)) {
			case "array" :
				for (var i = 0, len = action.length; i < len; i++) {
					if (Ext.type(action[i]) == "function") {
						action[i].call(scope, data, event, this);
					} else {
						throw ["Action: ", action[i], ' is not callable in "', this.current_state, '" state for event "', event].join("");
					}
				}
				break;
			case "function" :
				action.call(scope, data, event, this);
				break;
			default :
				throw ["Action: ", action, ' is not callable in "', this.current_state, '" state for event "', event].join("");
		}
	}
});
// ************************************************** Deppon.ux.UploadDialog ************************************************** //
Ext.define('Deppon.ux.UploadDialog', {
	statics: {
		STATE_QUEUE: 0,
		STATE_FINISHED: 1,
		STATE_FAILED: 2,
		STATE_PROCESSING: 3,
		title: 'File upload dialog',
		state_col_title: 'State',
		filename_col_title: 'Filename',
		note_col_title: 'Note',
		add_btn_text: 'Add',
		remove_btn_text: 'Remove',
		remove_btn_tip: 'Remove the aborted file from upload queue.',
		upload_btn_start_text: 'Upload',
		upload_btn_stop_text: 'Abort',
		upload_btn_start_tip: 'Upload queued files to the server.',
		upload_btn_stop_tip: 'Stop upload.',
		error_msgbox_title: 'Error',
		err_exceed_file_count: 'Exceed the limit of the file queue. Please select less than {0} file(s)',
		err_file_type_not_permitted: 'Selected file extension isn\'t permitted.<br/>Please select files with following extensions: {0}',
		note_queued_to_upload: 'Queued for upload.',
		note_processing: 'Uploading...',
		note_upload_failed: 'Server is unavailable or internal server error occured.',
		note_upload_success: 'OK.',
		note_upload_error: 'Upload error.',
		note_aborted: 'Upload aborted.',
		note_canceled: 'Upload canceled.'
	}
});
Ext.define('Deppon.ux.UploadDialog.BrowseButton', {
	extend: 'Ext.button.Button',
	alias: 'widget.browsebutton',
	input_name: "file",
	input_file: null,
	original_handler: null,
	original_scope: null,
	initComponent: function() {
		var me = this;
		me.callParent(arguments);
		me.original_handler = me.handler || null;
		me.original_scope = me.scope || window;
		me.handler = null;
		me.scope = null;
	},
	onRender: function() {
		this.callParent(arguments);
		this.createInputFile();
	},
	createInputFile: function() {
		var me = this,
			button_container = me.el;
		button_container.position('relative');
		me.input_file = Ext.DomHelper.append(button_container, {
			tag: "input",
			type: "file",
			size: 1,
			name: me.input_name || Ext.id(me.el),
			style: "position: absolute; display: block; border: none; cursor: pointer"
		}, true);
		me.input_file.setOpacity(0);
		me.adjustInputFileBox();
		me.input_file.on("change", me.onInputFileChange, me);
		me.input_file.on("click", function(e) {
			e.stopPropagation();
		});
	},
	autoWidth: function() {
		this.callParent(arguments);
		this.adjustInputFileBox();
	},
	adjustInputFileBox: function() {
		var me = this,
			button_container, button_box, 
			input_box, adj;
		if (me.el && me.input_file) {
			button_container = me.el;
			button_box = button_container.getBox();
			me.input_file.setStyle('font-size', (button_box.width * 0.5) + 'px');
			input_box = me.input_file.getBox();
			adj = {x: 3, y: 3};
			if (Ext.isIE) {
				adj = {x: 0, y: 3};
			}    
			me.input_file.setLeft(button_box.width - input_box.width + adj.x + 'px');
			me.input_file.setTop(button_box.height - input_box.height + adj.y + 'px');
		}
	},
	detachInputFile: function(no_create) {
		var me = this,
			result = me.input_file;
		no_create = no_create || false;
		me.input_file.removeAllListeners();
		me.input_file = null;
		if (!no_create) {
			me.createInputFile();
		}
		return result;
	},
	getInputFile: function() {
		return this.input_file;
	},
	disable: function() {
		this.callParent(arguments);
		this.input_file.dom.disabled = true;
	},
	enable: function() {
		this.callParent(arguments);
		this.input_file.dom.disabled = false;
	},
	destroy: function() {
		var input_file = this.detachInputFile(true);
		input_file.remove();
		input_file = null;
		this.callParent(arguments);
	},
	onInputFileChange: function() {
		if (this.original_handler) {
			this.original_handler.call(this.original_scope, this);
		}
	}
});
Ext.define('Deppon.ux.UploadDialog.Dialog', {
	extend: 'Ext.window.Window',
	alias: 'widget.uploaddialog',
	border: false,
    width: 450,
    height: 300,
    minWidth: 450,
    minHeight: 300,
    plain: true,
    constrainHeader: true,
    draggable: true,
    closable: true,
    maximizable: false,
    minimizable: false,
    resizable: true,
    autoDestroy: true,
	modal: true,
    closeAction: 'hide',
	layout: "fit",
    url: '',
    modulePath: '',
    base_params: {},
    permitted_extensions: [],
    file_upload_limit: 20,
    reset_on_hide: true,
    allow_close_on_upload: false,
    upload_autostart: false,
    post_var_name: 'file',
	fsa: null,
	state_tpl: null,
	form: null,
	grid_panel: null,
	is_uploading: false,
	initial_queued_count: 0,
	upload_frame: null,
	initComponent: function() {
		var me = this;
		me.callParent(arguments);
		me.setTitle(Deppon.ux.UploadDialog.title);
		var tt = {
			"created" : {
				"window-render" : [{
					action: [this.createForm, this.createGrid],
					state: "rendering"
				}],
				"destroy": [{
					action : this.flushEventQueue,
					state : "destroyed"
				}]
			},
			"rendering": {
				"grid-render": [{
					action: [this.fillToolbar, this.updateToolbar],
					state: "ready"
				}],
				"destroy": [{
					action: this.flushEventQueue,
					state: "destroyed"
				}]
			},
			"ready": {
				"file-selected": [{
					predicate: [this.isPermittedFile],
					action: this.addFileToUploadQueue,
					state: "adding-file"
				}, {}],
				"grid-selection-change": [{
					action: this.updateToolbar
				}],
				"remove-files": [{
					action: [this.removeFiles, this.fireFileRemoveEvent]
				}],
				"reset-queue": [{
					action: [this.resetQueue, this.fireResetQueueEvent]
				}],
				"start-upload": [{
					predicate: this.hasUnuploadedFiles,
					action: [this.setUploadingFlag, this.saveInitialQueuedCount, this.updateToolbar,
						this.prepareNextUploadTask, this.fireUploadStartEvent
					],
					state: "uploading"
				}, {}],
				"stop-upload": [{}],
				"hide": [{
					predicate: [this.isNotEmptyQueue, this.getResetOnHide],
					action: [this.resetQueue, this.fireResetQueueEvent]
				}, {}],
				"destroy": [{
					action: this.flushEventQueue,
					state: "destroyed"
				}]
			},
			"adding-file": {
				"file-added": [{
					predicate: this.isUploading,
					action: [this.incInitialQueuedCount, this.fireFileAddEvent],
					state: "uploading"
				}, {
					predicate: this.getUploadAutostart,
					action: [this.startUpload, this.fireFileAddEvent],
					state: "ready"
				}, {
					action: [this.updateToolbar, this.fireFileAddEvent],
					state: "ready"
				}]
			},
			"uploading": {
				"file-selected": [{
					predicate: [this.isPermittedFile],
					action: this.addFileToUploadQueue,
					state: "adding-file"
				}, {
					action: this.resetAddButton
				}],
				"grid-selection-change": [{}],
				"start-upload": [{}],
				"stop-upload": [{
					predicate: this.hasUnuploadedFiles,
					action: [this.resetUploadingFlag, this.abortUpload,this.updateToolbar, this.fireUploadStopEvent],
					state: "ready"
				}, {
					action: [this.resetUploadingFlag, this.abortUpload, this.updateToolbar,
						this.fireUploadStopEvent, this.fireUploadCompleteEvent
					],
					state: "ready"
				}],
				"file-upload-start": [{
					action: [this.uploadFile, this.findUploadFrame, this.fireFileUploadStartEvent]
				}],
				"file-upload-success": [{
					predicate: this.hasUnuploadedFiles,
					action: [this.resetUploadFrame, this.updateRecordState, this.prepareNextUploadTask, this.fireUploadSuccessEvent]
				}, {
					action: [this.resetUploadFrame, this.resetUploadingFlag,this.updateRecordState, this.updateToolbar,
						this.fireUploadSuccessEvent, this.fireUploadCompleteEvent
					],
					state: "ready"
				}],
				"file-upload-error": [{
					predicate: this.hasUnuploadedFiles,
					action: [this.resetUploadFrame, this.updateRecordState, this.prepareNextUploadTask, this.fireUploadErrorEvent]
				}, {
					action: [this.resetUploadFrame, this.resetUploadingFlag, this.updateRecordState, this.updateToolbar,
						this.fireUploadErrorEvent, this.fireUploadCompleteEvent
					],
					state: "ready"
				}],
				"file-upload-failed": [{
					predicate: this.hasUnuploadedFiles,
					action: [this.resetUploadFrame, this.updateRecordState, this.prepareNextUploadTask, this.fireUploadFailedEvent]
				}, {
					action: [this.resetUploadFrame, this.resetUploadingFlag, this.updateRecordState, this.updateToolbar,
						this.fireUploadFailedEvent, this.fireUploadCompleteEvent
					],
					state: "ready"
				}],
				"hide": [{
					predicate: this.getResetOnHide,
					action: [this.stopUpload, this.repostHide]
				}, {}],
				"destroy": [{
					predicate: this.hasUnuploadedFiles,
					action: [this.resetUploadingFlag, this.abortUpload, this.fireUploadStopEvent, this.flushEventQueue],
					state: "destroyed"
				}, {
					action: [this.resetUploadingFlag, this.abortUpload, this.fireUploadStopEvent,
						this.fireUploadCompleteEvent, this.flushEventQueue
					],
					state: "destroyed"
				}]
			},
			"destroyed": {}
		};
		me.fsa = new Ext.ux.Utils.FSA("created", tt, me);
		me.addEvents({
		  'fileadd': true,
		  'fileremove': true,
		  'resetqueue': true,
		  'uploadsuccess': true,
		  'uploaderror': true,
		  'uploadfailed': true,
		  'uploadcanceled': true,
		  'uploadstart': true,
		  'uploadstop': true,
		  'uploadcomplete': true,
		  'beforefileuploadstart': true,
		  'fileuploadstart': true
		});
		me.on('render', me.onWindowRender, me);
		me.on("beforehide", me.onWindowBeforeHide, me);
		me.on("hide", me.onWindowHide, me);
		me.on("destroy", me.onWindowDestroy, me);
		me.state_tpl = new Ext.Template(
			"<div class='ext-ux-uploaddialog-state ext-ux-uploaddialog-state-{state}'>&#160;</div>"
		).compile();
	},
	createForm: function() {
		this.form = Ext.DomHelper.append(this.getId() + '-body', {
			tag: 'form',
			method: 'post',
			action: this.url,
			style: 'position: absolute; left: -100px; top: -100px; width: 100px; height: 100px'
		})
	},
	createGrid: function() {
		var me = this,
			store, cloumns;
		columns =  [
			{ header: Deppon.ux.UploadDialog.state_col_title, width: 65, resizable : false, sortable: false, dataIndex: 'state', renderer: Ext.bind(me.renderStateCell, me) },
			{ header: Deppon.ux.UploadDialog.filename_col_title, width: 230, sortable: true, dataIndex: 'filename', renderer: me.renderFilenameCell },
			{ header: Deppon.ux.UploadDialog.note_col_title, width: 150, sortable: true, dataIndex: 'note', renderer: me.renderNoteCell }
		];
		store = Ext.create('Ext.data.Store', {
			fields: [ 'id', 'state', 'filename', 'note', 'input_element', 'relativePath' ],
			data: []
		});
		me.grid_panel = Ext.create('Ext.grid.Panel', {
			store: store,
			columns: columns,
			forceFit: true,
			dockedItems: [{
				xtype: 'toolbar',
				dock: 'top'
			}]
		});
		me.grid_panel.on("render", me.onGridRender, me);
		me.add(me.grid_panel);
		me.grid_panel.getSelectionModel().on("selectionchange", me.onGridSelectionChange, me);
	},
	fillToolbar: function() {
		var me = this,
			tb = me.grid_panel.getDockedItems('toolbar[dock="top"]')[0];
		tb.x_buttons = {};
		tb.x_buttons.add = tb.add({
			xtype: 'browsebutton',
			input_name: me.post_var_name,
			text: Deppon.ux.UploadDialog.add_btn_text,
			iconCls: "ext-ux-uploaddialog-addbtn",
			handler: me.onAddButtonFileSelected,
			scope: me
		});
		
		tb.x_buttons.remove = tb.add({
			text: Deppon.ux.UploadDialog.remove_btn_text,
			tooltip: Deppon.ux.UploadDialog.remove_btn_tip,
			iconCls: "ext-ux-uploaddialog-removebtn",
			handler: me.onRemoveButtonClick,
			scope: me
		});
		tb.add("-");
		tb.x_buttons.upload = tb.add({
			text: Deppon.ux.UploadDialog.upload_btn_start_text,
			tooltip: Deppon.ux.UploadDialog.upload_btn_start_tip,
			iconCls: "ext-ux-uploaddialog-uploadstartbtn",
			handler: me.onUploadButtonClick,
			scope: me
		});
	},
	updateToolbar: function() {
		var me = this,
			tb = me.grid_panel.getDockedItems('toolbar[dock="top"]')[0];
		Ext.suspendLayouts();
		if (me.is_uploading) {
			tb.x_buttons.remove.disable();
			tb.x_buttons.upload.enable();
			tb.x_buttons.upload.setIconCls("ext-ux-uploaddialog-uploadstopbtn");
			tb.x_buttons.upload.setText(Deppon.ux.UploadDialog.upload_btn_stop_text);
			tb.x_buttons.upload.setTooltip(Deppon.ux.UploadDialog.upload_btn_stop_tip);
		} else {
			tb.x_buttons.remove.enable();
			tb.x_buttons.upload.setIconCls("ext-ux-uploaddialog-uploadstartbtn");
			tb.x_buttons.upload.setText(Deppon.ux.UploadDialog.upload_btn_start_text);
			tb.x_buttons.upload.setTooltip(Deppon.ux.UploadDialog.upload_btn_start_tip);
			if (me.getQueuedCount() > 0) {
				tb.x_buttons.upload.enable();
			} else {
				tb.x_buttons.upload.disable();
			}
			if (me.grid_panel.getStore().getCount() > 0) {
				tb.x_buttons.remove.enable();
			} else {
				tb.x_buttons.remove.disable();
			}
		}
		Ext.resumeLayouts(true);
	},
	renderStateCell: function(value , metaData, record, rowIndex, columnIndex, store) {
		return this.state_tpl.apply({
			state: value
		});
	},
	renderFilenameCell: function(value , metaData, record, rowIndex, columnIndex, store) {
		metaData.tdAttr = 'data-qtip="' + value + '"';
		return value;
	},
	renderNoteCell: function(value , metaData, record, rowIndex, columnIndex, store) {
		metaData.tdAttr = 'data-qtip="' + value + '"';
		return value;
	},
	getFileExtension: function(filename) {
		var result = null,
			parts = filename.split(".");
		if (parts.length > 1) {
			result = parts.pop();
		}
		return result;
	},
	isPermittedFileType: function(filename) {
		var result = true,
			fileExtension,
			len = this.permitted_extensions.length;
		if (len > 0) {
			fileExtension = this.getFileExtension(filename).toLowerCase();
			for (var i=0; i<len; i++) {
				this.permitted_extensions[i] = this.permitted_extensions[i].toLowerCase();
			}
			result = this.permitted_extensions.indexOf(fileExtension) != -1;
		}
		return result;
	},
	isPermittedFile: function(browse_btn) {
		var result = false,
			filename = browse_btn.getInputFile().dom.value;
		if (this.isPermittedFileType(filename)) {
			result = true;
		} else {
			Ext.MessageBox.alert(Deppon.ux.UploadDialog.error_msgbox_title,
					Ext.String.format(Deppon.ux.UploadDialog.err_file_type_not_permitted, this.permitted_extensions.join(','))
			);
			result = false;
		}
		return result;
	},
	addFileToUploadQueue: function(browse_btn) {
		var input_file = browse_btn.detachInputFile(),
			store = this.grid_panel.getStore(),
			fileName;
		if(store.getCount() + 1 <= this.file_upload_limit) {
			input_file.appendTo(this.form);
			input_file.setStyle("width", "100px");
			input_file.dom.disabled = true;
			fileName = input_file.dom.value.split("\\").pop();
			store.add({
				id: Ext.data.IdGenerator.get('uuid').generate(),
				state: Deppon.ux.UploadDialog.STATE_QUEUE,
				filename: fileName,
				note: Deppon.ux.UploadDialog.note_queued_to_upload,
				input_element: input_file,
				relativePath: ''
			});
		} else {
			Ext.MessageBox.alert(Deppon.ux.UploadDialog.error_msgbox_title, 
				Ext.String.format(Deppon.ux.UploadDialog.err_exceed_file_count, this.file_upload_limit)
			);
		}
		this.fsa.postEvent("file-added", input_file.dom.value);
	},
	saveInitialQueuedCount: function() {
		this.initial_queued_count = this.getQueuedCount();
	},
	incInitialQueuedCount: function() {
		this.initial_queued_count++;
	},
	setUploadingFlag: function() {
		this.is_uploading = true;
	},
	resetUploadingFlag: function() {
		this.is_uploading = false;
	},
	prepareNextUploadTask: function() {
		var store = this.grid_panel.getStore(),
			record = null;
		store.each(function(r) {
			if (!record && r.get("state") == Deppon.ux.UploadDialog.STATE_QUEUE) {
				record = r;
			} else {
				r.get("input_element").dom.disabled = true;
			}
		});
		record.get("input_element").dom.disabled = false;
		record.set("state", Deppon.ux.UploadDialog.STATE_PROCESSING);
		record.set("note", Deppon.ux.UploadDialog.note_processing);
		record.commit();
		this.fsa.postEvent("file-upload-start", record);
	},
	removeFiles: function(file_records) {
		var store = this.grid_panel.getStore(),
			record;
		for (var size = store.getCount() - 1; size >= 0; size--) {
			record = store.getAt(size);
			if(record.get('state') != 1) {
				record.get("input_element").remove();
				store.remove(record);
			}
		}
	},
	resetQueue: function() {
		var store = this.grid_panel.getStore();
		store.each(function(record) {
			record.get("input_element").remove();
		});
		store.removeAll();
	},
	uploadFile: function(record) {
		var me = this;
		me.base_params = {
			id: record.get('id'),
			modulePath: me.modulePath,
			fileName: record.get('filename'),
			fileType: record.get('filename').split('.').pop()
		};
		Ext.Ajax.request({
			url: me.url,
			params: me.base_params || me.baseParams || me.params,
			method: "POST",
			form: me.form,
			isUpload: true,
			success: me.onAjaxSuccess,
			exception: me.onAjaxFailure,
			scope: me,
			record: record
		});
	},
	updateRecordState: function(data) {
		if ("success" in data.response && data.response.success) {
			data.record.set("state", Deppon.ux.UploadDialog.STATE_FINISHED);
			data.record.set("note", data.response.message || data.response.error || Deppon.ux.UploadDialog.note_upload_success);
			data.record.set("relativePath", data.response.relativePath);
		} else {
			data.record.set("state", Deppon.ux.UploadDialog.STATE_FAILED);
			data.record.set("note", data.response.message || data.response.error || Deppon.ux.UploadDialog.note_upload_error);
		}
		data.record.commit();
	},
	fireFileAddEvent: function(filename) {
		this.fireEvent("fileadd", this, filename);
	},
	fireUploadStartEvent: function() {
		this.fireEvent("uploadstart", this);
	},
	fireFileRemoveEvent: function() {
		this.fireEvent("fileremove", this);
	},
	fireResetQueueEvent: function() {
		this.fireEvent("resetqueue", this);
	},
	fireFileUploadStartEvent: function(record) {
		this.fireEvent("fileuploadstart", this, record.get("filename"));
	},
	fireUploadSuccessEvent: function(data) {
		this.fireEvent("uploadsuccess", this, data.record.get("filename"), data.response);
	},
	fireUploadErrorEvent: function(data) {
		this.fireEvent("uploaderror", this, data.record.get("filename"), data.response);
	},
	fireUploadFailedEvent: function(data) {
		this.fireEvent("uploadfailed", this, data.record.get("filename"));
	},
	fireUploadCompleteEvent: function() {
		this.fireEvent("uploadcomplete", this);
	},
	fireUploadStopEvent: function() {
		this.fireEvent("uploadstop", this);
	},
	findUploadFrame: function() {
		this.upload_frame = Ext.getBody().child("iframe.x-hidden:last");
	},
	resetUploadFrame: function() {
		this.upload_frame = null;
	},
	removeUploadFrame: function() {
		if (this.upload_frame) {
			this.upload_frame.removeAllListeners();
			this.upload_frame.dom.src = "about:blank";
			this.upload_frame.remove();
		}
		this.upload_frame = null;
	},
	abortUpload: function() {
		this.removeUploadFrame();
		var store = this.grid_panel.getStore(),
			record = null;
		store.each(function(r) {
			if (r.get("state") == Deppon.ux.UploadDialog.STATE_PROCESSING) {
				record = r;
				return false;
			}
		});
		record.set("state", Deppon.ux.UploadDialog.STATE_FAILED);
		record.set("note", Deppon.ux.UploadDialog.note_aborted);
		record.commit();
	},
	repostHide: function() {
		this.fsa.postEvent("hide");
	},
	flushEventQueue: function() {
		this.fsa.flushEventQueue();
	},
	onWindowRender: function() {
		this.fsa.postEvent("window-render");
	},
	onWindowBeforeHide: function() {
		return this.isUploading() ? this.getAllowCloseOnUpload() : true;
	},
	onWindowHide: function() {
		this.fsa.postEvent("hide");
	},
	onWindowDestroy: function() {
		this.fsa.postEvent("destroy");
	},
	onGridRender: function() {
		this.fsa.postEvent("grid-render");
	},
	onGridSelectionChange: function() {
		this.fsa.postEvent("grid-selection-change");
	},
	onAddButtonFileSelected: function(btn) {
		this.fsa.postEvent("file-selected", btn);
	},
	onUploadButtonClick: function() {
		if (this.is_uploading) {
			this.fsa.postEvent("stop-upload");
		} else {
			this.fsa.postEvent("start-upload");
		}
	},
	onRemoveButtonClick: function() {
		this.fsa.postEvent("remove-files");
	},
	onResetButtonClick: function() {
		this.fsa.postEvent("reset-queue");
	},
	onCloseButtonClick: function() {
		this[this.closeAction].call(this);
	},
	onAjaxSuccess: function(response, options) {
		var json_response = {
			success: false,
			error: Deppon.ux.UploadDialog.note_upload_error
		};
		try {
			var rt = response.responseText;
			var filter = rt.match(/^<[^>]+>((?:.|\n)*)<\/[^>]+>$/);
			if (filter) {
				rt = filter[1];
			}
			json_response = Ext.JSON.decode(rt);
		} catch (e) {
			throw "Excepion appeared in the [Success Function] of [Ajax Request].";
		}
		var data = {
			record: options.record,
			response: json_response
		};
		if ("success" in json_response && json_response.success) {
			this.fsa.postEvent("file-upload-success", data);
		} else {
			this.fsa.postEvent("file-upload-error", data);
		}
	},
	onAjaxFailure: function(response, options) {
		var json = Ext.decode(response.responseText),
			message = Deppon.ux.UploadDialog.note_upload_failed;
		if(!Ext.isEmpty(json)&&!Ext.isEmpty(json.message)){
			message = message + ":" + json.message;
		}
		var data = {
			record: options.record,
			response: {
				success: false,
				error: message
			}
		};
		this.fsa.postEvent("file-upload-failed", data);
	},
	startUpload : function() {
		this.fsa.postEvent("start-upload")
	},
	stopUpload: function() {
		this.fsa.postEvent("stop-upload");
	},
	getUrl: function() {
		return this.url;
	},
	setUrl: function(url) {
		this.url = url;
	},
	getBaseParams: function() {
		return this.base_params;
	},
	setBaseParams: function(params) {
		this.base_params = params;
	},
	getUploadAutostart: function() {
		return this.upload_autostart;
	},
	setUploadAutostart: function(value) {
		this.upload_autostart = value;
	},
	getAllowCloseOnUpload: function() {
		return this.allow_close_on_upload;
	},
	setAllowCloseOnUpload: function(value) {
		this.allow_close_on_upload = value;
	},
	getResetOnHide: function() {
		return this.reset_on_hide;
	},
	setResetOnHide: function(value) {
		this.reset_on_hide = value;
	},
	getPermittedExtensions: function() {
		return this.permitted_extensions;
	},
	setPermittedExtensions: function(value) {
		this.permitted_extensions = value;
	},
	isUploading: function() {
		return this.is_uploading;
	},
	isNotEmptyQueue: function() {
		return this.grid_panel.getStore().getCount() > 0;
	},
	getQueuedCount: function(count_processing) {
		var count = 0,
			store = this.grid_panel.getStore();
		store.each(function(record) {
			if (record.get('state') == Deppon.ux.UploadDialog.STATE_QUEUE) {
				count++;
			}
			if (count_processing && record.get('state') == Deppon.ux.UploadDialog.STATE_PROCESSING) {
				count++;
			}
		});
		return count;
	},
	hasUnuploadedFiles: function() {
		return this.getQueuedCount() > 0;
	}
});

/**
 * @author xiantao zeng
 * @since 2012-12-27
 * @memo 多文件上传面板组件
 */
Ext.define('Deppon.ux.FileUploadGrid', {
	extend: 'Ext.grid.Panel',
	alias: 'widget.fileuploadgrid',
	statics: {
		file_name_header: 'File Name',
		file_type_header: 'Extension',
		down_load_tip: 'Download File',
		preview_load_tip: 'Preview Image File',
		upload_btn_text: 'Upload',
		delete_btn_text: 'Delete',
		warn_msgbox_title: 'Warning',
		warn_del_message: 'Are you sure to delete the file?',
		warn_notsel_message: 'Please select at least one file to oprate!',
		img_preview_title: 'Image Preview'
	},
	frame: true,
	enableColumnHide: false,
	sortableColumns: false,
	reviewFlag: false,
	uploadWin: null,
	uploadUrl: '',
	downLoadUrl: '',
	deleteUrl: '',
	imgReviewUrl: '',
	modulePath: '',
	initComponent: function() {
		var me = this;
		me.columns =  [
			{ header: Deppon.ux.FileUploadGrid.file_name_header, flex: 1, sortable: true, dataIndex: 'fileName' },
			{ header: Deppon.ux.FileUploadGrid.file_type_header, width: 60, sortable: true, dataIndex: 'fileType' },
			{
				xtype: 'actioncolumn',
				width: 50,
				items: [{
					iconCls: 'deppon_icons_download',
					tooltip: Deppon.ux.FileUploadGrid.down_load_tip,
					handler: me.onDownloadFile,
					scope: me
				}, {
					tooltip: Deppon.ux.FileUploadGrid.preview_load_tip,
					handler: me.onPreviewImgFile,
					scope: me,
					getClass: me.getReviewCls
				}]
			}
		];
		me.store = Ext.create('Ext.data.Store', {
			fields: [ 'id', 'fileName', 'fileType', 'relativePath', 'status' ],
			data: []
		});
		me.dockedItems = [{
			xtype: 'toolbar',
			hidden: me.reviewFlag,
			dock: 'top',
			items: [{ 
				xtype: 'button',
				text: Deppon.ux.FileUploadGrid.upload_btn_text,
				handler: me.onUploadFiles,
				scope: me
			}, {
				xtype: 'button',
				text: Deppon.ux.FileUploadGrid.delete_btn_text,
				handler: me.onDeleteFile,
				scope: me
			}]
		}];
		me.callParent(arguments);
	},
	onUploadFiles: function() {
		var me = this,
			grid, store;
		if(me.uploadWin == null) {
			me.uploadWin = Ext.create('Deppon.ux.UploadDialog.Dialog', {
				closeAction: 'hide',
				reset_on_hide: true,
				allow_close_on_upload: true,
				permitted_extensions: me.fileTypes || [],
				url: me.uploadUrl || '',
				modulePath: me.modulePath || '',
				file_upload_limit: me.file_upload_limit || 20
			});
			me.uploadWin.on('beforehide', me.saveAttachementInfo, me);
		}
		me.uploadWin.show();
	},
	saveAttachementInfo: function() {
		var me = this,
			uploadWin = me.uploadWin,
			grid = uploadWin.down('grid'),
			store = grid.store,
			count = store.getCount(),
			record;
		if(count > 0) {
			for(var i = 0; i < count; i++) {
				record = store.getAt(i);
				if(record.get('state') == 1) {
					me.store.add({
						id: record.get('id'),
						fileName: record.get('filename'),
						fileType: record.get('filename').split('.').pop(),
						relativePath: record.get('relativePath'),
						status: 'add'
					})
				}
			}
		}
	},
	onDeleteFile: function() {
		var me = this,
			store = me.store,
			sm = me.getSelectionModel(),
			id;
		if(sm.hasSelection()) {
			Ext.MessageBox.confirm(Deppon.ux.FileUploadGrid.warn_msgbox_title, Deppon.ux.FileUploadGrid.warn_del_message, function(btn) {
				if(btn == 'yes') {
					var store = me.store,
						sm = me.getSelectionModel(),
						selections = sm.getSelection(),
						record = selections[0],
						id = record.get('id'),
						relativePath = record.get('relativePath');
					Ext.Ajax.request({
						url: me.deleteUrl || '',
						method: 'post',
						params: {
							id: id,
							relativePath: relativePath
						},
						success: function(response) {
							store.remove(record);
						},
						exception: function(response) {
							var json = Ext.decode(response.responseText);
							Ext.ux.Toast.msg(Deppon.ux.FileUploadGrid.warn_msgbox_title, json.message, 'error', 2000);
						}
					});
				}
			});
		} else {
			Ext.MessageBox.alert(Deppon.ux.FileUploadGrid.warn_msgbox_title, Deppon.ux.FileUploadGrid.warn_notsel_message);
		}
	},
	onDownloadFile: function(grid, rowIndex, colIndex) {
		var me = this,
			record = grid.store.getAt(rowIndex);
			fileName = record.get('fileName'),
			relativePath = record.get('relativePath');
		window.open(me.downLoadUrl+'?fileName='+fileName+'&relativePath='+relativePath);
	},
	onPreviewImgFile: function(grid, rowIndex, colIndex) {
		var me = this;
		if(me.imgReviewUrl) {
			var record = grid.store.getAt(rowIndex),
				relativePath = record.get('relativePath');
			if(!me.reviewWin) {
				me.reviewWin = Ext.create('Ext.window.Window', {
					title: Deppon.ux.FileUploadGrid.img_preview_title,
					width: 600,
					height: 400,
					modal: true,
					resizable: false,
					layout: 'auto',
					closeAction: 'hide'
				})
			}
			Ext.suspendLayouts();
			me.reviewWin.removeAll(true);
			me.reviewWin.add({
				xtype: 'image',
				src: me.imgReviewUrl+'?relativePath='+relativePath
			});
			Ext.resumeLayouts(true);
			me.reviewWin.down('image').on('render', me.onRenderImage, me.reviewWin);
			Ext.defer(function() {
				this.reviewWin.show();
			}, 500, me);
		}
	},
	onRenderImage: function() {
		var me = this,
			image = me.down('image'),
			imageEl = image.getEl();
		imageEl.dom.onload = function() {
			var img = this,
				win = me,
				winWidth , winHeight,
				imageWidth, imageHeight;
			winWidth = Ext.fly(win.getId() + "-body").getWidth() - 4;
			winHeight = Ext.fly(win.getId() + "-body").getHeight() - 4;
			imageWidth = img.width;
			imageHeight = img.height;
			if(winWidth > imageWidth && winHeight > imageHeight) {
				//return;
			} else if(winWidth < imageWidth && winHeight > imageHeight) {
				img.width = winWidth;
				img.height = winWidth * imageHeight / imageWidth;
			} else if(winWidth > imageWidth && winHeight < imageHeight) {
				img.width = imageWidth * winHeight / imageHeight;
				img.height = winHeight;
			} else if(winWidth < imageWidth && winHeight < imageHeight) {
				if(imageWidth / imageHeight > winWidth / winHeight) {
					img.width = winWidth;
					img.height = winWidth * imageHeight / imageWidth;
				} else if(imageWidth / imageHeight < winWidth / winHeight) {
					img.width = imageWidth * winHeight / imageHeight;
					img.height = winHeight;
				}
			}
			win.setBodyStyle("text-align:center;top:50%;margin-top:-"+img.height/2+"px");
			win = null;
		}
	},
	getReviewCls: function(value, metadata, record, rowIndex) {
		var type = record.get('fileType'),
			reg = /(jpg|jpeg|gif|bmp|png)$/ig,
			isImg = reg.test(type);
		if(isImg) {
			return "deppon_icons_showdetail";
		}
	}
});

//读取文件ImageUpload.js
﻿
Ext.define('Deppon.form.HtmlEditor.imageUpload', {
	alias: 'plugin.imageUpload',
	alternateClassName: 'Ext.ux.form.HtmlEditor.imageUpload',
    /**
     * @cfg {Array} options
     * Associative array with all the strings.
     * If not specified it will show all the strings in english
     */
    lang: {
        'Insert/Edit Image': '插入图片',
        'Upload Image...': '浏览',
        'Uploading your photo...': '上传中...',
        'Error': 'Error',
        'Focus first': '请设置要插入图片的位置',
        'OK': '保存',
        'Cancel': '关闭',
        'Confirmation': '',
        'Are you sure you want to delete this image?': '',
        'Your photo has been uploaded.': '上传成功'
    },

    /**
     * @cfg {String} submitUrl
     * Path to the upload script.
     * Default 'htmlEditorImageUpload.php'
     */
    submitUrl: 'htmlEditorImageUpload.php',
	
	/**
     * @cfg {String} serverSideEdit
     * Enables/disables server side image editing buttons.
     * Default false
     */
    disableServerSideEdit: false,
	
	/**
     * @cfg {String} serverSideEdit
     * Enables/disables server side image deletion.
     * Default false
     */
    disableDelete: false,
	
	/**
     * @cfg {String} styling
     * Enables/disables image css styling.
     * Default false
     */
    disableStyling: false,

    /**
     * @cfg {String} mamangerUrl
     * Path to the image manager script.
     * Default 'htmlEditorImageManager.php'
     */
    managerUrl: 'htmlEditorImageUpload.php',


    /**
     * @cfg {integer} pageSize
     * Number of images to show on the list.
     * Default 6
     */
    pageSize: 6,

  /**
   * @cfg {Boolean} values are:
   * true : Default
   * Allows the user to resize an image clicking on it and dragging with the mouse. (Only WebKit browsers)
   * false 
   * The image wont be resized if the user drags on it
   */
    dragResize: true,
	
	  /**
   * @cfg {Boolean} values are:
   * false : Default
   * Context menu for images enabled
   * true 
   * Context menu will not be avaible
   */
    enableContextMenu: false,

/**
   * @cfg {Boolean} values are:
   * true : Default
   * Allows the user to resize an image clicking on it and using the mousewheel. (Only WebKit browsers & Opera)
   * 
   * false 
   * The image wont be resized if the user uses mousewheel on it
   */
    wheelResize: true,

    /**
     * @cfg {String} iframeCss
     * Path to the iframe css file. 
     * It's important to do not merge this css with other CSS files, because it will be applied to the htmleditor 
     * iframe head. If more css rules are included, it can suffer undesired effects
     * Default 'css/iframe_styles.css'
     */
    iframeCss: 'css/iframe_styles.css',
    t: function (string) {
        return this.lang[string] ? this.lang[string] : string;
    },
	
    constructor: function (config) {
        Ext.apply(this, config);
        this.callParent(arguments);
    },
	
    init: function (panel) {
        this.cmp = panel;
        this.cmp.on('render', this.onRender, this);
        this.cmp.on('initialize', this.initialize, this);
        this.cmp.on('beforedestroy', this.beforeDestroy, this);
    },
	
    initialize: function () {
        var me = this;
        var cmpDoc = this.cmp.getDoc();
        me.flyDoc = Ext.fly(cmpDoc);

        // Inject custom css file to iframe's head in order to simulate image control selector on click, over webKit and Opera browsers
//        if ((Ext.isWebKit || Ext.isOpera)) 
//        	me._injectCss(me.cmp, me.iframeCss);

        // attach context menu
        if(me.enableContextMenu)
        	//me._contextMenu();

		// attach events to control when the user interacts with an image
		me.cmp.mon(me.flyDoc, 'dblclick', me._dblClick, me, {delegate : "img"});
		me.cmp.mon(me.flyDoc, 'mouseup', me._docMouseUp, me);
		me.cmp.mon(me.flyDoc, 'paste', me._removeSelectionHelpers, me);
		
        // mousewheel resize event
        if ((Ext.isWebKit || Ext.isOpera) && me.wheelResize) {	
			me.cmp.mon(me.flyDoc, 'mousewheel', me._wheelResize, me, {delegate : "img"});
        }

        // mouse drag resize event
        if (Ext.isWebKit && me.dragResize) {	
			me.cmp.mon(me.flyDoc, 'drag', me._dragResize, me, {delegate : "img"});
        }
    },
	
    beforeDestroy: function () {
        var me = this;
        if (me.uploadDialog) me.uploadDialog.destroy();
        if (me.contextMenu) me.contextMenu.destroy();
    },
	
    onRender: function () {

        var me = this;
		var imageButton = Ext.create('Ext.button.Button', {
            iconCls: 'x-htmleditor-imageupload',
            handler: me._openImageDialog,
            scope: me,
            tooltip: me.t('Insert/Edit Image'),
            overflowText: me.t('Insert/Edit Image')
        });

        // we save a reference to this button to use it later
        me.imageButton = imageButton;

        me.cmp.getToolbar().add(imageButton);

    },
	
    //private
    // instead of overriding the htmleditor header method we just append another css file to it's iframe head
    _injectCss: function (cmp, cssFile) {
        var frameName = cmp.iframeEl.dom.name;
        var iframe;

        if (document.frames) iframe = document.frames[frameName];
        else iframe = window.frames[frameName];

        // we have to add our custom css file to the iframe
        var ss = iframe.document.createElement("link");
        ss.type = "text/css";
        ss.rel = "stylesheet";
        ss.href = cssFile;

        if (document.all) iframe.document.createStyleSheet(ss.href);
        else iframe.document.getElementsByTagName("head")[0].appendChild(ss);

    },
	
    // private
    _dblClick: function (evt) {
        var me = this;
        var target = evt.getTarget();

        if (target.tagName == "IMG") {
            me._openImageDialog();
        }
    },
	
    //private
    _openImageDialog: function () {

        var me = this;
        var doc = this.cmp.getDoc();
        var win = this.cmp.win;
        var sel = "";
        var range = "";
        var image = "";
        var imagesList = doc.body.getElementsByTagName("IMG");
        var imagesListLength = imagesList.length;

        //insertAtCursor function is completely useless for this purpose, so I need to write all this stuff to insert html at caret position	
        // I need to know if the browser uses the W3C way or the Internet Explorer method
        var ieBrowser = doc.selection && doc.selection.createRange ? true : false;
        var nonIeBrowser = win.getSelection && win.getSelection().getRangeAt ? true : false;

        if (nonIeBrowser) {
            sel = win.getSelection();
            // if focus is not in htmleditor area
            try {
                range = sel.getRangeAt(0);
            } catch (err) {
            	Ext.Msg.alert('', me.t('Focus first'));
//                win.focus();
//                range = sel.getRangeAt(0);
                return;
            }

        } else if (ieBrowser) {
            //it's compulsory to get the focus before creating the range, if not we'll lose the caret position
            win.focus();
            sel = doc.selection;
            range = sel.createRange();
        }
        
        me.uploadDialog = Ext.create('Ext.ux.form.HtmlEditor.ImageDialog', {
            lang: me.lang,
            t: me.t,
            submitUrl: me.submitUrl,
            managerUrl: me.managerUrl,
            iframeDoc: doc,
            imageToEdit: image,
            pageSize: me.pageSize,
            imageButton: me.imageButton,
			disableServerSideEdit: me.serverSideEdit,
			disableStyling: me.styling,
			disableDelete : me.disableDelete
        });

        me.uploadDialog.on('close', function () {
            if (Ext.isIE) {
                me.imageButton.toggle(false);
                me._removeSelectionHelpers();
            }
        }, me);

        // custom event that fires when the user presses the ok button on the dialog
        me.uploadDialog.on('imageloaded', function () {

            var newImage = this.getImage();

            // if it's an edited image, we have to replace it with the new values
            if (image != "") {
                for (var i = 0; i < imagesListLength; i++) {
                    if (parseInt(imagesList[i].getAttribute('iu_edit')) > 0) {
                        if (nonIeBrowser) {
                            imagesList[i].parentNode.replaceChild(newImage, imagesList[i]);
                            try {
                                if (sel) {
                                    sel.selectAllChildren(doc.body);
                                    sel.collapseToStart();
                                }

                            } catch (ex) {};
                        } else if (ieBrowser) {
                            imagesList[i].outerHTML = newImage.outerHTML;
                        }
                        break;
                    }
                }
            }
            // if not we just insert a new image on the document
            else {
                if (nonIeBrowser) {
                    range.insertNode(newImage);
                } else if (ieBrowser) {
                    win.focus();
                    range.select();
                    range.pasteHTML(newImage.outerHTML);
                }
            }

            me.imageToEdit = "";
            this.close();
            me.imageButton.toggle(false);
        });

        me.uploadDialog.show();
    },
	
    //private
	//Remove custom image attrs from the iframe body DOM
    _removeSelectionHelpers: function () {
        var me = this;
        var imagesList = me.cmp.getDoc().body.getElementsByTagName("IMG");
        var imagesListLength = imagesList.length;

        for (var i = 0; i < imagesListLength; i++) {
            imagesList[i].removeAttribute('iu_edit');
        }
    },
	
	//private
	//When user uses mousewheel over an image
    _wheelResize: function (e) {
        var target = e.getTarget();
        if (target.tagName == "IMG" && target.getAttribute('iu_edit') == 1) {
            var delta = e.getWheelDelta();
            var width = target.style.width ? parseInt(target.style.width.replace(/[^\d.]/g, "")) : target.width;

            target.removeAttribute('height');
            target.style.removeProperty('height');

            // change just width to keep aspect ratio
            target.style.width = (delta < 1) ? width - 10 : width + 10;

            e.preventDefault();
        } else return;
    },
	
	//private
	//When user drags over an image
    _dragResize: function (e) {

        var target = e.getTarget();

        if (target.tagName == "IMG" && (target.getAttribute('iu_edit') == 1)) {
            var width = e.getX() - target.offsetLeft;
            var height = e.getY() - target.offsetTop;
            target.style.width = width + "px";
            target.style.height = height + "px";
            e.preventDefault();
        } else return;
    },
    
	//private
	//When user clicks on content editable area
    _docMouseUp: function (evt) {

        var me = this;
        var target = evt.getTarget();

        me._removeSelectionHelpers();

        if (target.tagName == "IMG") {
            me.imageButton.toggle(true);
            if ((me.wheelResize || me.dragResize) && (Ext.isWebKit || Ext.isOpera)) target.setAttribute('iu_edit', '1');
            else target.setAttribute('iu_edit', '2');

            // select image. 
            // On safari if we copy and paste the image, class attrs are converted to inline styles. It's a browser bug.
            if (Ext.isWebKit) {
                var sel = this.cmp.getWin().getSelection ? this.cmp.getWin().getSelection() : this.cmp.getWin().document.selection;
                sel.setBaseAndExtent(target, 0, target, 1);
            }
        } else me.imageButton.toggle(false);
    }
});

Ext.define('Deppon.form.HtmlEditor.ImageDialog', {
    extend: 'Ext.window.Window',
    alternateClassName: 'Ext.ux.form.HtmlEditor.ImageDialog',
    lang: null,
    lang: null,
    t: null,
    submitUrl: null,
    managerUrl: null,
    iframeDoc: null,
    pageSize: null,
    imageToEdit: '',
    closeAction: 'destroy',
    width: 460,
    modal: true,
    resizable: false,
    relativePath: null,
    layout: {
        type: 'fit'
    },
    title: '',
    listeners: {
        show: function (panel) {
            // we force the focus on the dialog window to avoid control artifacts on IE
            //panel.down('[name=src]').focus();
        },
        resize: function (panel) {
            panel.center();
        }
    },
	
    initComponent: function () {
        var me = this;
        me.items = [{
            xtype: 'form',
            name: 'imageUploadForm',
            bodyPadding: 10,
            items: [{
                xtype: 'filefield',
                buttonOnly: false,
                msgTarget: 'side',
                readOnly:true,
                readOnlyCls:'disable',
                allowBlank: false,
                name: 'file',
                anchor: '100%',
                buttonText: me.t('Upload Image...')
            }],
			dockedItems: [{
                xtype: 'container',
                dock: 'bottom',
                padding: 4,
                items: [{
                    xtype: 'button',
                    style: {
                        'float': 'right'
                    },
                    text: me.t('Cancel'),
                    handler: me.close,
                    scope: me
                }, {
                    xtype: 'button',
                    style: {
                        'float': 'right',
                        'margin-right': '8px'
                    },
                    text: me.t('OK'),
                    formBind: true,
                    handler: me._uploadImage,
                    scope: me
                }]
            }]
        }];
		
        me.callParent(arguments);
        me.setTitle(me.t('Insert/Edit Image'));
    },
	
	/**
     * Returns the current image with all the data specified in the form. (Size, borders, padding e.t.c)
     * @return {HTMLImageObject} 
     */
    getImage: function () {
    	var me = this;
    	var image = this.iframeDoc.createElement("img");
    	image.setAttribute('src', me.managerUrl + '?relativePath=' + me.relativePath);
    	
        return image;
    },
	
	//private
	_deleteImage:function (ev, a) {
		
		var me = this;
		
		if (!me.disableDelete){
			Ext.Msg.show({
				title: me.t('Confirmation'),
				msg: me.t('Are you sure you want to delete this image?'),
				buttons: Ext.Msg.YESNO,
				closable: false,
				fn: function (btn) {
					if (btn == 'yes') {
						Ext.Ajax.request({
							url: me.managerUrl,
							method: 'POST',
							params: {
								'action': 'delete',
								'image': a.getAttribute ? a.getAttribute('img_fullname') : a
							},
							success: function (response) {
								
								var result = Ext.JSON.decode(response.responseText);
								if(result.success)
								{
									// delete the image from the list
									var combo = me.down('[name=src]');

									//if I do here a combo.store.load() to refresh, the paging toolbar disappears
									// so I'll do it on combo expand event
									combo.needsRefresh = true;

									combo.setValue('');
									me.down('form').getForm().reset();
								}else{
									Ext.Msg.alert(me.t('Error'), 'Error: ' + result.errors);
								}
							}
						});
					}
				}
			});
		}
	},
	
	//private
	//method to upload the image to the server
    _uploadImage: function (button) {
        var me = this;

        var form = button.up('form').getForm();
        if (form.isValid()) {
            form.submit({
                url: me.submitUrl + '?action=upload',
                waitMsg: me.t('Uploading your photo...'),
                success: function (fp, o) {
                    me.relativePath = o.result.attachement.relativePath;
                    me.fireEvent('imageloaded');
                },
                failure: function (form, action) {
                    if (action.result) Ext.Msg.alert(me.t('Error'), 'Error: ' + action.result.errors);
                    me.down('[name=file]').reset();
                }
            });
        }
    },
	
	//private
	_serverAction: function (params) {

        var me = this;

		Ext.Ajax.request({
			url: me.managerUrl,
			method: 'POST',
			params: params,
			success: function (response) {
				
				var result = Ext.JSON.decode(response.responseText);
				
				if(result.success)
				{
					var combo = me.down('[name=src]');
                    combo.needsRefresh = true;
                    combo.setRawValue(result.data['src']);
				}else{
					Ext.Msg.alert('Error', 'Error: ' + result.errors);
				}
			}
		});
	},
	
	//private
	_deleteImageClick: function(){
		var me = this;
		var src = me.down('[name=src]').getValue();
		var imageName = src.substring(src.lastIndexOf('/')+1);
		me._deleteImage(null,imageName); 
	}

});

//读取文件LinebreakColumn.js
/**
 * @class Deppon.grid.column.LineBreak
 * @extends Ext.grid.column.Column
 * <p>实现gird中列内容的换行显示功能</p>
 * <p>用法:在gird中需要换行的列的xtype:linebreakcolumn<p>
 * <p><u>示例代码:</u><p>
 * <pre><code>       
 * @example        
 * Ext.create('Ext.data.Store', {
 *	storeId:'simpsonsStore',
 *	fields:['name', 'email', 'phone'],
 *	data:{'items':[
 *		{ 'name': 'Lisa',  "email":"123123123-12312-12312-3123lisa@simpsons.com",  "phone":"555-111-1224" },
 *		{ 'name': 'Bart',  "email":"bart@simpsons.com",  "phone":"555-222-1234"  },
 *		{ 'name': 'Homer', "email":"home@simpsons.com",  "phone":"555-222-1244"  },
 *		{ 'name': 'Marge', "email":"marge@simpsons.com", "phone":"555-222-1254"  }
 *	]},
 *	proxy: {
 *		type: 'memory',
 *		reader: {
 *			type: 'json',
 *			root: 'items'
 *		}
 *	}
 * });
 * Ext.create('Ext.grid.Panel', {
 *	title: 'linebreakcolumn demo',
 *	frame: true,
 *	store: Ext.data.StoreManager.lookup('simpsonsStore'),
 *	columns: [
 *		{ text: 'Name',  dataIndex: 'name',align: 'left', flex: 1,xtype : 'linebreakcolumn' },
 *		{ text: 'Email', dataIndex: 'email' ,align: 'left', flex: 1,xtype : 'linebreakcolumn' },
 *		{ text: 'Phone', dataIndex: 'phone', align: 'left',flex: 1,xtype : 'linebreakcolumn' }
 *	],
 *	height: 200,
 *	width: 300,	
 *	renderTo:Ext.getBody()
 * });	
 * </code></pre> 
 */
Ext.define('Deppon.grid.column.LineBreak', {
	extend: 'Ext.grid.column.Column',
	alias: 'widget.linebreakcolumn',
	alternateClassName: ['Deppon.grid.LineBreakColumn'],
	initComponent: function() {
		var me = this,
			customerRenderer = me.renderer;
		if(customerRenderer) {
			me.renderer = function(value, metadata, record, rowIndex, columnIndex, store) {
				value = customerRenderer(value, metadata, record, rowIndex, columnIndex, store);
				value = me.defaultRenderer(value, metadata, record, rowIndex, columnIndex, store);
				return value;
			};
		}
		me.callParent(arguments);
	},
	defaultRenderer: function(value, metadata, record, rowIndex, columnIndex, store) {
		//增加word-break: break-all; CSS实现单词也实现换行
		metadata.style = 'white-space: normal; overflow: visible; word-break: break-all;';
		return value;
	}
});

//读取文件MonthDateField.js
/**
 *	@docauthor 钟庭杰 <tingjiezhong@gmail.com>
 *
 *	年月日期选择控件。
 *
 *
 *	# 组件示例
 *	<pre><code>
 *	@example
 *	Ext.create('Ext.form.Panel', {
 *		renderTo: Ext.getBody(),
 *		width: 580,
 *		bodyPadding: 10,
 *		title: '年月日期选择组件示例',
 *		items: [{
 *			fieldLabel: '日期时间',
 *			xtype: 'monthdatefield',
 *			format: "Y-m",
 *			startDay: new Date()
 *		}]
 *	});
 *	</code></pre>
 */
Ext.define('Deppon.form.field.MonthDateField', {
    extend: 'Ext.form.field.Picker',
    alias: 'widget.monthdatefield',

	/**
     * @cfg {String} format
     * 默认的日期格式字符串，可以根据使用场景的需要对其进行重写。 这个格式必须是符合Ext.Date.parse格式要求的字符串。
     */
    format: "Y-m",

	/**
     * @cfg {String} altFormats
     * 使用‘|’区分的多种时间格式，这些格式被用来验证用户的输入项是否是匹配的时间格式。
     */
    altFormats: "m/y|m/Y|m-y|m-Y|my|mY|y/m|Y/m|y-m|Y-m|ym|Ym",

	/**
     * @cfg {String} altFormats
     * trigger按钮的附加样式。trigger按钮默认使用'x-form-trigger'样式， 如果指定triggerCls属性的话将appended上原有的样式上。 (默认展示一个日期图标).
	 * Defaults to: "x-form-date-trigger"
     */
    triggerCls: Ext.baseCSSPrefix + 'form-date-trigger',

	/**
     * @cfg {Boolean} matchFieldWidth
     * 下拉选择器(picker)的宽度是否要准确地和表单项的宽度一致。默认值是false。
     */
    matchFieldWidth: false,

	/**
     * @cfg {Number} [startDay=new Date()]
     * 开始日期。
	 * 默认是0(星期天).
     */
    startDay: new Date(),

    initComponent: function () {
        var me = this;

        me.disabledDatesRE = null;

        me.callParent();
    },

	/**
     * 初始化控件的值
     */
    initValue: function () {
        var me = this,
            value = me.value;

        if (Ext.isString(value)) {
            me.value = Ext.Date.parse(value, this.format);
        }
        if (me.value)
            me.startDay = me.value;
        me.callParent();
    },

	/**
	 * @method
     * 将表单项的原始值转换成复合类型，以适合于特殊的单表类型。允许控制标准化和用户输入值向单项类型适用值的转换。 
	 * 例如，对于Ext.form.field.DateDate型对象，是被getValue调用。
     * 由用户实现决定对于不能成功转换成目标类型的原始值的处理。
	 * 对于相反的转换，请参见See {@link #valueToRaw}
     *
     * 此类没有实现转换，返回一个未处理的原始值。
	 *
     * @param {Object} rawValue
     * @return {Object} value 转换后的值。
     */
    rawToValue: function (rawValue) {
        return Ext.Date.parse(rawValue, this.format) || rawValue || null;
    },

	/**
	 * @method
     * 将复合类型的值转换成适合于在表单中显示的原始值。此项控制了被传入setValue的值向用户展示的方式，包括本地化。 
	 * 例如，对于Ext.form.field.Date类型，它决定了一个Date对象传入到 setValue将被转化成一个字符串符以供在表单中展示。
     * 对于相反的转换，请参见See {@link #rawToValue}
     *
     * 基类仅仅实现了标准的字符串转换，将{@link Ext#isEmpty empty values}转换成一个空的字符串。
	 *
     * @param {Object} value 将要转化成原始值的复合类型值。
     * @return {Object} value 转换后的值。
     */
    valueToRaw: function (value) {
        return this.formatDate(value);
    },

	/**
	 * @method
     * 格式化数据
	 *
     * @param {Object} date 将要转化值格式化成组件定义的格式。
     * @return {Object} value 转化后的值。
     */
    formatDate: function (date) {
        return Ext.isDate(date) ? Ext.Date.dateFormat(date, this.format) : date;
    },
	
	/**
     * @method
     * 创建一个年月选择的下拉框
     */
    createPicker: function () {
        var me = this,
            format = Ext.String.format;

        return Ext.create('Ext.picker.Month', {
        	cls: 'c-monthpicker',
            pickerField: me,
            ownerCt: me.ownerCt,
            renderTo: document.body,
            floating: true,
            shadow: false,
            focusOnShow: true,
            listeners: {
                scope: me,
                cancelclick: me.onCancelClick,
                okclick: me.onOkClick,
                yeardblclick: me.onOkClick,
                monthdblclick: me.onOkClick
            }
        });
    },

	/**
     * @method
     * 展开下拉框
     */
    onExpand: function () {
        //this.picker.show();
        this.picker.setValue(this.startDay);
        //
        
    },
    
	/**
     * @method
     * 点击确定按钮，选择年月值并隐藏下拉面板
     * @param {Object} picker 下拉面板对象。
     * @return {Object} value 选择的日期值。
     */
    onOkClick: function (picker, value) {
        var me = this,
            month = value[0],
            year = value[1],
            date = new Date(year, month, 1);
        me.startDay = date;
        me.setValue(date);
        this.picker.hide();
        //this.blur();
    },

	/**
     * @method
     * 点击取消按钮，取消选择年月值并隐藏下拉面板
     */
    onCancelClick: function () {
        this.picker.hide();
        //this.blur();
    }

});

//读取文件OpenTipWindowColumn.js
/**
 * @class Deppon.grid.column.OpenTipWindow
 * @extends Ext.grid.column.Column
 * <p>实现gird中列内容的富提示（传入自定义组件显示在提示浮动窗体中）功能</p>
 * <p>用法:在gird中需要换行的列的xtype:opentipwindowcolumn<p>
 * <p><u>示例代码:</u><p>
 * <pre><code>
 * @example
 * Ext.define('Deppon.Dept.Window',{
 *  extend: 'Ext.window.Window',
 *  width: 340,
 *  title: '用户详细信息',
 *  height: 260,
 *  closable: true,
 *  closeAction: 'hide',
 *  userForm: null,
 *  getUserForm: function(){
 *      if(this.userForm==null){
 *          this.userForm = Ext.create('Deppon.Dept.DeptForm');
 *      }
 *      return this.userForm;
 *  },
 *  bindData: function(record,cellIndex,rowIndex){
 *      this.getUserForm().getForm().loadRecord(record);
 *  },
 *  constructor: function(config){
 *      var me = this,
 *          cfg = Ext.apply({}, config);
 *      me.items=[me.getUserForm()];
 *      me.callParent([cfg]);
 *  }
 * });   
 * Ext.define('Deppon.Dept.DeptForm',{
 *   extend: 'Ext.form.Panel',
 *   frame:true,
 *   height: 180,
 *   width: 300,
 *   defaults: {
 *       margin:'5 10 5 10',
 *       anchor: '90%',
 *       labelWidth:60
 *   },
 *   defaultType : 'textfield',
 *   items : [{
 *       name: 'superId',
 *       fieldLabel: '上级id'
 *   },{
 *       name: 'deptId',
 *       fieldLabel: '部门id'
 *   },{
 *       name: 'deptName',
 *       fieldLabel: '名称'
 *   }],
 *   bindData: function(record){
 *       this.getForm().loadRecord(record);
 *   },
 *   constructor: function(config){
 *       var me = this,
 *           cfg = Ext.apply({}, config);
 *       me.callParent([cfg]);
 *   }
 * });
 * Ext.create('Ext.data.Store', {
 *   storeId:'dept',
 *   fields:['deptId', 'deptName', 'superId'],
 *   data:{'items':[
 *       { 'deptId': 'dept1',  "deptName":"deptName1",  "superId":"superId1" },
 *       { 'deptId': 'dept2',  "deptName":"deptName2",  "superId":"superId2"  }
 *   ]},
 *   proxy: {
 *       type: 'memory',
 *       reader: {
 *           type: 'json',
 *           root: 'items'
 *       }
 *   }
 * });
 * Ext.create('Ext.grid.Panel', {
 *   title: 'opentipwindowcolumn demo',
 *   frame: true,
 *   store: Ext.data.StoreManager.lookup('dept'),
 *   columns: [
 *       { text: 'deptId', dataIndex: 'deptId'},
 *       { text: 'deptName', dataIndex: 'deptName'},
 *       { text: 'superId', dataIndex: 'superId' ,
 *           tipConfig: {
 *               maxWidth: 425,
 *               width: 340,
 *               height: 230
 *           },
 *           tipBodyElement:'Deppon.Dept.DeptForm',
 *           flex: 1,
 *           windowClassName: 'Deppon.Dept.Window',
 *           xtype: 'opentipwindowcolumn',
 *       }
 *   ],
 *   height: 200,
 *   width: 400,
 *   renderTo:Ext.getBody()
 * });
 * </code></pre>
 */
 
Ext.define('Deppon.grid.column.OpenTipWindow', {
	extend: 'Ext.grid.column.Column',
	alias: 'widget.opentipwindowcolumn',
	alternateClassName: 'Deppon.grid.opentipwindowcolumn',
	/**
	 * @cfg {String} windowClassName
	 * windowClassName 显示窗体对象名称
	 */
	windowClassName: null,
	
	/**
	 * @private
	 * @cfg {Ext.window.Window} openWindow
	 * 自定义窗口的类对象
	 */
	openWindow: null,
	
	/**
	 * @cfg {String} tipBodyElement
	 * 设置Tip的Body中显示的组件类名
	 * 为了实现tip内组件数据的动态绑定，在Grid的信息预览的数据时，一般由某一行的record对象，在执行binData方法的时候，有以下参数可以使用(和grid的Column的renderer属性的参数相同)</br>
	 * a)	record 当前行的record对象</br>
	 * b)	value 当前单元格的值</br>
	 * c)	metadata 当前单元格的元数据</br>
	 * d)	store 当前grid的store对象</br>
	 * e)	view 当前view
	 */
	tipBodyElement: null,

	/**
	 * @cfg {Object} tipConfig
	 * tipConfig 基本信息配置
	 */
	tipConfig: null,
	
	/**
	 * @private
	 * @cfg tipBody
	 * tip内显示的对象
	 */
	tipBody: null,
	
	/**
	 * @private
	 * @cfg tip
	 * tip显示框对象
	 */
	tip: null,

	/**
	 * @private
	 * @cfg hasListener
	 * 是否已经绑定事件
	 */
	hasListener: false,

	/**
	 * @method
	 * 得到打开的窗口对象
	 * 窗口内的组件如果需要动态绑定数据，则可以在窗口空实现一个bindData的方法，
	 * 方法的参数有两个：
	 * 第一个虽record,当前点击行的行记录对象；
	 * 第二个是grid,表格对象；
	 * @return {Ext.window.Window} 返回创建的窗口对象。
	 */
	getOpenWindow: function(){
		if(this.openWindow == null) {
			this.openWindow = Ext.create(this.windowClassName);
		}
		return this.openWindow;
	},

	/**
	 * @private
	 * @method
	 * 显示窗口信息
	 */
	showOpenWindow: function(grid, view, td, cellIndex, record, tr, rowIndex, e, eOpts) {
		if(this.windowClassName && (typeof this.getOpenWindow().bindData) != 'undefined') {
			this.getOpenWindow().bindData(record, grid);
		}
		this.getOpenWindow().show();
	},

	initComponent: function(config) {
		var me = this,
			customerRenderer = me.renderer;
		if(customerRenderer) {
			me.renderer = function(value, metadata, record, rowIndex, columnIndex, store, view) {
				value = customerRenderer(value, metadata, record, rowIndex, columnIndex, store, view);
				value = me.defaultRenderer(value, metadata, record, rowIndex, columnIndex, store, view);
				return value;
			};
		}
		me.defaultRenderer = function(value, metadata, record, rowIndex, columnIndex, store, view) {
			var me = this,
				grid = me.up('gridpanel'),
				tdId = record.internalId + '-tip-openwindow-td';
			if(Ext.isEmpty(me.tipBody)) {
				if(!Ext.isEmpty(me.tipBodyElement)){
					me.tipBody = Ext.create(me.tipBodyElement);
				}
			}
			if(me.tip==null) {
				if(me.tipConfig == null) {
					me.tipConfig = {};
				}
				if(!Ext.isEmpty(me.tipBody)) {
					me.tipConfig.items = [me.tipBody];
				}
				me.tip = Ext.create('Ext.tip.ToolTip', me.tipConfig);
			}
			metadata.tdAttr = "id=" +'"'+tdId+'"';
	
			if(!me.hasListener) {
				var evenObject,evenName;
				if(store.getProxy().type=='ajax' || store.getProxy().type=='jsonp' || store.getProxy().type=='direct') {
					evenObject = store;
					evenName = 'load';
				} else {
					//修改之前view的事件viewready为refresh
					evenObject = view;
					evenName = 'refresh';
				}
				evenObject.on(evenName, function(){
					me.addMouseOverLis(value, metadata, record, store, view);
				});
				view.up('grid').on('sortchange', function(){
					me.addMouseOverLis(value, metadata, record, store, view);
				});
				me.hasListener = true;
			}
			if(me.windowClassName == null) {
				Ext.Error.raise("'windowClassName' of A property must configuration.");
			}
			grid.on('cellclick', function(view, td, cellIndex, clickRecord, tr, clickRowIndex, e, eOpts) {
				if(td!=null&&td.className!=null){
					if(td.className.indexOf('opentipwindowcolumn')!=-1) {
						me.showOpenWindow(grid, view, td, cellIndex, clickRecord, tr, clickRowIndex, e, eOpts);
					}
				}
			}, this);
			return Ext.String.format(
					'<a href="javascript:void(0)">{0}</a>',
					value
			);
		};
		me.callParent(arguments);
	},
	
	/**
	 * @private
	 * @method
	 * 增加鼠标移动到当前单元格上时，显示tip信息的事件
	 */
	addMouseOverLis: function(value, metadata, record, store, view) {
		var me = this;
		store.each(function(record, index, length) {
			var td = Ext.get(record.internalId+'-tip-openwindow-td');
			td.on('mouseover',function( e, element, eOpts ){
				//在定义行体的时候，装入的对象如果要进行数据的绑定，可以通过实现这个方法，进行实现
				if(me.tipBody&&(typeof me.tipBody.bindData)!='undefined'){
					me.tipBody.bindData(record, value, metadata, store, view);
				}
				me.tip.showAt([e.browserEvent.pageX+10,e.browserEvent.pageY+10]);
			});
		});
	}
});

//读取文件OpenWindowColumn.js
/**
 * @class Deppon.grid.column.OpenWindow
 * @extends Ext.grid.column.Column
 * <p>实现gird中列内容的富提示(传入自定义组件显示在提示浮动窗体中)功能</p>
 * <p><u>示例代码:</u></p>
 * <pre><code> 
 * @example
 * Ext.create('Ext.data.Store', {
 * 	storeId:'simpsonsStore',
 * 	fields:['name', 'email', 'phone'],
 * 	loadSimpsonsStoreEmail:'',	
 * 	setLoadSimpsonsStoreEmail : function(loadSimpsonsStoreEmail) {
 * 		this.loadSimpsonsStoreEmail = loadSimpsonsStoreEmail;
 * 	},
 * 	getLoadSimpsonsStoreEmail : function() {
 * 		return this.loadSimpsonsStoreEmail;
 * 	},
 * 	data:{'items':[
 * 		{ 'name': 'Lisa',  "email":"lisa@simpsons.com",  "phone":"555-111-1224"  },
 * 		{ 'name': 'Bart',  "email":"bart@simpsons.com",  "phone":"555-222-1234" },
 * 		{ 'name': 'Homer', "email":"home@simpsons.com",  "phone":"555-222-1244"  },
 * 		{ 'name': 'Marge', "email":"marge@simpsons.com", "phone":"555-222-1254"  }
 * 	]},
 * 	proxy: {
 * 		type: 'memory',
 * 		reader: {
 * 		type: 'json',
 * 			root: 'items'
 * 		}
 * 	},
 * 	constructor : function(config) {
 * 		var me = this, cfg = Ext.apply({}, config);
 * 			me.callParent([ cfg ]);
 * 	},
 * 	listeners : {
 * 		beforeload : function(store, operation, eOpts) {
 * 			Ext.apply(operation, {
 * 				params : {
 * 					'items.email' : store.getLoadSimpsonsStoreEmail()
 * 				}
 * 			});
 * 		}
 * 	}
 * });
 * Ext.define('dpap.openwindowcolumn.grid', {
 * 	extend:'Ext.form.Panel',
 * 	title: 'openwindowcolumn  grid',
 * 	frame:true,	
 * 	height: 160,
 * 	width: 300,
 * 	defaults: {
 * 		margin:'5 10 5 10',
 * 		anchor: '90%',
 * 		labelWidth:60
 * 	},
 * 	defaultType : 'textfield',
 * 	layout:'column',
 * 	items: [{ 
 * 		name: 'name',
 * 		fieldLabel: 'Name',
 * 		readOnly:true,
 * 		columnWidth:1
 * 	},{
 * 		name: 'email',
 * 		fieldLabel: 'Email',
 * 		columnWidth:1,
 * 		readOnly:true
 * 	},{ 
 * 		name: 'phone',
 * 		fieldLabel: 'Phone',
 * 		columnWidth:1,
 * 		readOnly:true
 * 	}],
 * 	bindData: function(record){
 * 		this.getForm().loadRecord(record);
 * 	},
 * 	constructor: function(config){
 * 		var me = this,
 * 			cfg = Ext.apply({}, config);
 * 		me.callParent([cfg]);
 * 	}
 * });
 * Ext.create('Ext.grid.Panel', {
 * 	title: 'openwindowcolumn demo',
 * 	frame:true,
 * 	store: Ext.data.StoreManager.lookup('simpsonsStore'),	
 * 	columns: [
 * 		{ header: 'Name',  dataIndex: 'name' },
 * 		{ header: 'Email', dataIndex: 'email', flex: 1 ,
 * 			xtype: 'openwindowcolumn', 
 * 			windowClassName:'dpap.openwindowcolumn.Window' 
 * 		},
 * 		{ header: 'Phone', dataIndex: 'phone' }
 * 	],
 * 	height: 200,
 * 	width: 400,
 * 	renderTo:Ext.getBody()
 * });
 * 
 * Ext.define('dpap.openwindowcolumn.Window', {
 * 	extend : 'Ext.window.Window',
 * 	title : 'openwindowcolumn Window ',
 * 	modal : true,
 * 	closeAction : 'hide',
 * 	width : 335,
 * 	height : 230,
 * 	openwindowcolumnForm : null,
 * 	getOpenwindowcolumnForm : function(){
 * 		if(this.openwindowcolumnForm == null){
 * 			this.openwindowcolumnForm = Ext.create('dpap.openwindowcolumn.grid');
 * 		}	
 * 		return this.openwindowcolumnForm;
 * 	},
 * 	bindData : function(record, cellIndex, rowIndex){
 * 		this.getOpenwindowcolumnForm().getForm().loadRecord(record);
 * 	},
 * 	initComponent :function(config){
 * 		var me = this ,cfg = Ext.apply({}, config);
 * 		me.items = [ me.getOpenwindowcolumnForm() ];
 * 		me.callParent(cfg);
 * 	}
 * });
 * </code></pre> 
 */
Ext.define('Deppon.grid.column.OpenWindow', {
	extend: 'Ext.grid.column.Column',
	alias: 'widget.openwindowcolumn',
	alternateClassName: 'Deppon.grid.OpenWindowColumn',
	
	/**
	 * @cfg {String} windowClassName
	 * windowClassName 数据显示窗体对象名称
	 */
	windowClassName: null,
	
	/**
	 * @private
	 * @cfg {Ext.window.Window} openWindow
	 * 自定义窗口的类对象
	 */
	openWindow: null,
	
	/**
	 * @method
	 * 得到打开的窗口对象
	 * 窗口内的组件如果需要动态绑定数据，则可以在窗口空实现一个bindData的方法，
	 * 方法的参数有两个：
	 * 第一个虽record,当前点击行的行记录对象；
	 * 第二个是grid,表格对象；
	 * @return {Ext.window.Window} 返回创建的窗口对象。
	 */
	getOpenWindow: function(){
		if(this.openWindow == null) {
			this.openWindow = Ext.create(this.windowClassName);
		}
		return this.openWindow;
	},
	
	/**
	 * @private
	 * @method
	 * 显示窗口信息
	 */
	showOpenWindow: function(grid, view, td, cellIndex, record, tr, rowIndex, e, eOpts) {
		if(this.windowClassName && (typeof this.getOpenWindow().bindData) != 'undefined') {
			this.getOpenWindow().bindData(record, grid);
		}
		this.getOpenWindow().show();
		},
	initComponent: function(config) {
		var me = this,
			customerRenderer = me.renderer;
		if(customerRenderer) {
			me.renderer = function(value, metadata, record, rowIndex, columnIndex, store) {
				value = customerRenderer(value, metadata, record, rowIndex, columnIndex, store);
				value = me.defaultRenderer(value, metadata, record, rowIndex, columnIndex, store);
				return value;
			};
		};
		me.callParent(arguments);
	},
	defaultRenderer: function(value, metadata, record, rowIndex, columnIndex, store) {
		var me = this,
			grid = me.up('gridpanel');
		if(me.windowClassName == null) {
			Ext.Error.raise("'windowClassName' of A property must configuration.");
		}
		grid.on('cellclick', function(view, td, cellIndex, clickRecord, tr, clickRowIndex, e, eOpts) {
			if(td!=null&&td.className!=null){
				if(td.className.indexOf('openwindowcolumn')!=-1) {
					me.showOpenWindow(grid, view, td, cellIndex, clickRecord, tr, clickRowIndex, e, eOpts);
				}
			}
		}, this);
		return Ext.String.format(
				'<a href="javascript:void(0)">{0}</a>',
				value
		);
	}
});

//读取文件PageSizePlugin.js
/**
 *	@docauthor 曾宪涛 <rxjh2ht@163.com>
 *
 *	此插件用于分页工具栏上，可以通过此插件动态改变Store加载的分页大小。
 *
 *	插件的分页大小默认为：[10, 25, 50, 100, 200]，可以通过插件的sizeArray属性自定义分页大小选项。
 *
 *	而且插件提供输入分页大小的功能，为防止输入数值过大影响服务器性能，提供最大分页数限制，默认最大分页大小为200。
 *
 *	# 插件示例
 *	<pre><code>
 *	@example
 *	Ext.define('ForumThread', {
 *      extend: 'Ext.data.Model',
 *      fields: [
 *          'title', 'forumtitle', 'forumid', 'username',
 *          {name: 'replycount', type: 'int'},
 *          {name: 'lastpost', mapping: 'lastpost', type: 'date', dateFormat: 'timestamp'},
 *          'lastposter', 'excerpt', 'threadid'
 *      ],
 *      idProperty: 'threadid'
 *  });
 *  var store = Ext.create('Ext.data.Store', {
 *		autoLoad: true,
 *      pageSize: 5,
 *      model: 'ForumThread',
 *      proxy: {
 *          type: 'jsonp',
 *          url: 'http://www.sencha.com/forum/topics-browse-remote.php',
 *          reader: {
 *              root: 'topics',
 *              totalProperty: 'totalCount'
 *          }
 *      }
 *  });
 *	Ext.create('Ext.grid.Panel', {
 *      width: 720,
 *      height: 190,
 *      title: '动态设定分页大小插件示例',
 *      store: store,
 *      disableSelection: true,
 *      loadMask: true,
 *		autoScroll: true,
 *		columnLines: true,
 *      frame: false,
 *      columns:[{
 *          text: "Topic",
 *          dataIndex: 'title',
 *          flex: 1,
 *          sortable: false
 *      },{
 *          text: "Author",
 *          dataIndex: 'username',
 *          width: 100,
 *          hidden: true,
 *          sortable: true
 *      },{
 *          text: "Replies",
 *          dataIndex: 'replycount',
 *          width: 70,
 *          align: 'right',
 *          sortable: true
 *      },{
 *         text: "Last Post",
 *         dataIndex: 'lastpost',
 *         width: 150,
 *         sortable: true
 *      }],
 *		dockedItems: [{
 *			xtype: 'pagingtoolbar',
 *			store: store,
 *			dock: 'bottom',
 *			displayInfo: true,
 *			plugins: {
 *				ptype: 'pagesizeplugin',
 *				//超出输入最大限制是否提示，true则提示，默认不提示
 *				alertOperation: true,
 *				//自定义分页comobo数据
 *				sizeSize: [['2', 2], ['5', 5], ['10', 10], ['20', 20]],
 *				//入最大限制，默认为200
 *				maximumSize: 100
 *			}
 *		}],
 *      renderTo: Ext.getBody()
 *  });
 *	</code></pre>
 */
Ext.define('Deppon.ux.PageSizePlugin', {
	alias: 'plugin.pagesizeplugin',
	/**
     * @cfg {Number} maximumSize
     * 设置输入有效分页大小的最大值。
     */
	maximumSize: 200,
	/**
     * @cfg {String} limitWarning
     * 设置输入分页大小超过限制后的提示信息。
     */
	limitWarning: 'Can not exceed the maximum page size：',
	/**
     * @cfg {String} beforeText
     * 设置插件左边显示的文本。
     */
	beforeText: 'Each page display',
	/**
     * @cfg {String} afterText
     * 设置插件右边显示的文本。
     */
	afterText: 'records',
	/**
     * @cfg {Array} sizeList
     * 设置自定义的分页大小选项，如果不设置，则分页大小选项默认为：[10, 25, 50, 100, 200]。
     */
	sizeList: [['10', 10], ['25', 25], ['50', 50], ['100', 100], ['200', 200]],
	/**
	 * @private
     * 构造函数，初始化此插件的配置项。
     */
	constructor: function(config) {
		var me = this;
		Ext.apply(me, config);
	},

	/**
	 * @private
     * 插件初始化方法，适用于分页组件上。在组件的initComponent方法执行完毕后调用。
     */
	init: function(paging) {
		var me = this;
		me.combo = me.getPageSizeCombo(paging);
		paging.add(' ', me.beforeText, me.combo, me.afterText, ' ');
		me.combo.on('select', me.onChangePageSize, me);
		me.combo.on('keypress', me.onKeyPress, me);
	},
	/**
	 * @method
     * 返回分页组件上动态设定分页大小的combobox组件。此方法在插件初始化的时候自动调用。
	 * @param {Ext.toolbar.Paging} 分页组件。
     * @return {Ext.form.field.ComboBox} 返回插件的分页大小Combobox组件。
     */
	getPageSizeCombo: function(paging) {
		var me = this,
			defaultValue = paging.pageSize || paging.store.pageSize || 25;
		return Ext.create('Ext.form.field.ComboBox', {
			store: new Ext.data.SimpleStore({
				fields: ['text', 'value'],
				data: me.sizeList
			}),
			mode: 'local',
			displayField: 'text',
			valueField: 'value',
			allowBlank: true,
			triggerAction: 'all',
			width: 50,
			maskRe: /[0-9]/,
			enableKeyEvents: true,
			value: defaultValue
		});
	},
	/**
	 * @private
     * 分页大小Combobox组件的select监听事件，当选择分页大小时，设定store的pageSize属性为所选择的大小，且重新加载。
	 * @param {Ext.form.field.ComboBox} 分页大小Combobox组件。
     */
	onChangePageSize: function(combo) {
		var paging = combo.up('standardpaging') || combo.up('pagingtoolbar'),
			store = paging.store,
			comboSize = combo.getValue();
		store.pageSize = comboSize;
		store.loadPage(1);
	},
	/**
	 * @private
     * 分页大小Combobox组件的keypress监听事件，实现输入大小，回车设定store的pageSize属性为所输入的大小，且重新加载。
	 * 输入大小有最大限制，超出最大限制的输入无效。
	 * @param {Ext.form.field.Text} 分页大小Combobox组件里的text组件。
	 * @param {Ext.EventObject} 分页大小Combobox组件。
     */
	onKeyPress: function(field, e) {
		if(Ext.isEmpty(field.getValue())) {
			return;
		}
		var me = this,
			fieldValue = field.getValue(),
			paging = me.combo.up('standardpaging') || me.combo.up('pagingtoolbar'),
			store = paging.store;
		if(e.getKey() == e.ENTER) {
			if(fieldValue <= me.maximumSize) {
				store.pageSize = fieldValue;
				store.loadPage(1);
			} else {
				Ext.MessageBox.alert('Warning', me.limitWarning + me.maximumSize);
				field.setValue('');
			}
		}
	},
	/**
	 * @private
     * 销毁插件相关的属性和对象。该方法会在组件销毁的时候自动调用。
     */
	destory: function() {
		var me = this;
		me.combo.clearListeners();
		Ext.destroy(me.combo);
		delete me.combo;
	}
});

//读取文件RadioModel.js
/**
 *	@docauthor 曾宪涛 <rxjh2ht@163.com>
 *
 *	此组件实现Radio样式的单选模式。
 *
 *	此组件只提供单选模式，不支持通过model等配置实现多选模式。
 *
*	# 组件示例
 *	<pre><code>
 *	@example
 *	Ext.define('Company', {
 *      extend: 'Ext.data.Model',
 *      fields: [
 *		   {name: 'company'},
 *		   {name: 'price',      type: 'float', convert: null,     defaultValue: undefined},
 *		   {name: 'change',     type: 'float', convert: null,     defaultValue: undefined},
 *		   {name: 'pctChange',  type: 'float', convert: null,     defaultValue: undefined},
 *		   {name: 'lastChange', type: 'date',  dateFormat: 'n/j h:ia', defaultValue: undefined}
 *      ],
 *      idProperty: 'company'
 *  });
 *	var myData = [
 *      ['3m Co',                               71.72, 0.02,  0.03,  '9/1 12:00am'],
 *      ['Alcoa Inc',                           29.01, 0.42,  1.47,  '9/1 12:00am'],
 *      ['Altria Group Inc',                    83.81, 0.28,  0.34,  '9/1 12:00am'],
 *      ['American Express Company',            52.55, 0.01,  0.02,  '9/1 12:00am'],
 *      ['American International Group, Inc.',  64.13, 0.31,  0.49,  '9/1 12:00am'],
 *      ['AT&T Inc.',                           31.61, -0.48, -1.54, '9/1 12:00am'],
 *      ['Boeing Co.',                          75.43, 0.53,  0.71,  '9/1 12:00am'],
 *      ['Caterpillar Inc.',                    67.27, 0.92,  1.39,  '9/1 12:00am'],
 *      ['Citigroup, Inc.',                     49.37, 0.02,  0.04,  '9/1 12:00am'],
 *      ['E.I. du Pont de Nemours and Company', 40.48, 0.51,  1.28,  '9/1 12:00am'],
 *      ['Exxon Mobil Corp',                    68.1,  -0.43, -0.64, '9/1 12:00am'],
 *      ['General Electric Company',            34.14, -0.08, -0.23, '9/1 12:00am'],
 *      ['General Motors Corporation',          30.27, 1.09,  3.74,  '9/1 12:00am'],
 *      ['Hewlett-Packard Co.',                 36.53, -0.03, -0.08, '9/1 12:00am']
 *	];
 *	var store = Ext.create('Ext.data.ArrayStore', {
 *		model: 'Company',
 *		data: myData
 *	});
 *	Ext.create('Ext.grid.Panel', {
 *      width: 720,
 *      height: 190,
 *      title: '单选模式示例',
 *      store: store,
 *		autoScroll: true,
 *		columnLines: true,
 *      frame: true,
 *      columns:[{
 *          text: "Company",
 *          dataIndex: 'company',
 *          flex: 1,
 *          sortable: false
 *      },{
 *          text: "Price",
 *          dataIndex: 'price',
 *          width: 75,
 *          sortable: true
 *      },{
 *          text: "Change",
 *          dataIndex: 'change',
 *          width: 75,
 *          sortable: true
 *      },{
 *         text: "% Change",
 *         dataIndex: 'pctChange',
 *         width: 75,
 *         sortable: true
 *      },{
 *         text: "Last Updated",
 *         dataIndex: 'lastChange',
 *         width: 85,
 *         sortable: true
 *      }],
 *      selModel: Ext.create('Ext.selection.RadioModel'),
 *      renderTo: Ext.getBody()
 *  });
 *	</code></pre>
 */
Ext.define('Deppon.selection.RadioModel', {
    alias: 'selection.radiomodel',
	alternateClassName: 'Ext.selection.RadioModel',
    extend: 'Ext.selection.RowModel',
    /**
     * @cfg {String} mode
     * 选择模式配置，默认且唯一为SINGLE，即单选模式。
     */
    mode: 'SINGLE',
    /**
     * @cfg {Number/String} [injectCheckbox=0]
     * 插入radio列的索引，默认为0，即第一列。
     * 支持所有有效的索引数值，且支持字符'first'和'last'。
     */
    injectRadio: 0,
    /**
     * @cfg {Boolean} radioOnly
     * 如果设置为true，则只能通过点击radio来选中当前行，默认为false.
     */
    radioOnly: false,
    /**
     * @cfg {Boolean} showHeaderRadio
     * 如果设置为true，则显示标题列的radio，默认为false。
     */
    showHeaderRadio: false,
    /**
     * @cfg {Number} headerWidth
     * radio列的宽度，默认为24px。
     */
    headerWidth: 24,
	/**
	 * @private
     * 绑定radio列于相应组件。
	 * @param {Ext.view.View} 通常为grid的view。
     */
    bindComponent: function(view) {
        var me = this;
        me.sortable = false;
        me.callParent(arguments);
        if (!me.hasLockedHeader() || view.headerCt.lockedCt) {
            me.addRadio(true);
            me.mon(view.ownerCt, 'reconfigure', me.onReconfigure, me);
        }
    },
	/**
	 * @method
     * 判断是否包含锁定的列头。
     * @return {Boolean} 如果包含，返回true，反之返回false。
     */
    hasLockedHeader: function() {
        var views = this.views,
            vLen = views.length,
            v;
        for (v = 0; v < vLen; v++) {
            if (views[v].headerCt.lockedCt) {
                return true;
            }
        }
        return false;
    },
    /**
     * @private
     * 增加radio按钮于标题列上。
     * @param {Boolean} 如果第一次绑定，则初始化为true值。
     */
    addRadio: function(initial) {
        var me = this,
            radio = me.injectRadio,
            view = me.views[0],
            headerCt = view.headerCt;
        if (radio !== false) {
            if (radio == 'first') {
                radio = 0;
            } else if (radio == 'last') {
                radio = headerCt.getColumnCount();
            }
            Ext.suspendLayouts();
            headerCt.add(radio, me.getHeaderConfig());
            Ext.resumeLayouts();
        }
        if (initial !== true) {
            view.refresh();
        }
    },
    /**
     * Handles the grid's reconfigure event.  Adds the checkbox header if the columns have been reconfigured.
     * @private
	 * grid的reconfigure监听方法。如果columns被重新设置则增加radio列的头radio按钮。
     * @param {Ext.panel.Table} grid
     * @param {Ext.data.Store} store
     * @param {Object[]} columns
     */
    onReconfigure: function(grid, store, columns) {
        if(columns) {
            this.addRadio();
        }
    }, 
	/**
	 * @method
     * 返回列头的配置信息。
     * @return {Oject} 列头的配置信息。
     */
    getHeaderConfig: function() {
        var me = this,
            showRadio = me.showHeaderRadio = false;
        return {
            isRadiorHd: showRadio,
            text : '&#160;',
            width: me.headerWidth,
            sortable: false,
            draggable: false,
            resizable: false,
            hideable: false,
            menuDisabled: true,
            dataIndex: '',
            cls: '',
            renderer: Ext.Function.bind(me.renderer, me),
            editRenderer: me.editRenderer || me.renderEmpty,
            locked: me.hasLockedHeader()
        };
    },
	/**
	 * @protected
     * 空单元格的渲染配置。
     * @return {String} 返回空格显示。
     */
    renderEmpty: function() {
        return '&#160;';    
    },
	/**
	 * @protected
     * radio样式单元格的渲染配置。
     * @return {String} 返回radio样式的div。
     */
    renderer: function(value, metaData, record, rowIndex, colIndex, store, view) {
        var baseCSSPrefix = Ext.baseCSSPrefix;
        metaData.tdCls = baseCSSPrefix + 'grid-cell-special ' + baseCSSPrefix + 'grid-cell-row-radio';
        return '<div class="' + baseCSSPrefix + 'grid-row-radio">&#160;</div>';
    },
	// override
    onRowMouseDown: function(view, record, item, index, e) {
        view.el.focus();
        var me = this,
            radior = e.getTarget('.' + Ext.baseCSSPrefix + 'grid-row-radio'),
            mode;
        if (!me.allowRightMouseSelection(e)) {
            return;
        }
        if (me.radioOnly && !radior) {
            return;
        }
        if (radior) {
            mode = me.getSelectionMode();
            if (mode !== 'SINGLE') {
                me.setSelectionMode('SINGLE');
            }
            me.selectWithEvent(record, e);
            me.setSelectionMode(mode);
        } else {
            me.selectWithEvent(record, e);
        }
    }
});

//读取文件RangeDateField.js
/**
 *	@docauthor 曾宪涛 <rxjh2ht@163.com>
 *
 *	期间段组件，封装起始日期与终止日期两个控件，并提供时间间隔、是否允许为空等配置。
 *
 *	期间段组件的子组件类型可以配置，可以是DateField组件、TimeField组件、DateTimeField组件等。
 *
 *	# 组件示例
 *	<pre><code>
 *	@example
 *	Ext.create('Ext.form.Panel', {
 *		renderTo: Ext.getBody(),
 *		width: 580,
 *		bodyPadding: 10,
 *		title: '区间段组件示例',
 *		items: [{
 *			fieldLabel: '日期时间',
 *			xtype: 'rangedatefield',
 *			dateType: 'my97datetimefield',
 *			//my97datetimefield组件显示所必须的配置。
 *			fieldId: 'my97datetimefield',
 *			//区间间隔，以天为单位。
 *			dateRange: 60,
 *			//起始日期组件的name属性。
 *			fromName: 'formDateTime',
 *			//终止日期组件的name属性。
 *			toName: 'toDateTime',
 *			//起始日期组件的初始值。
 *			fromValue: '2012-05-01',
 *			//终止日期组件的初始值。
 *			toValue: '2012-06-01',
 *			//不允许为空
 *			disallowBlank: true
 *		},{
 *			fieldLabel: '日期',
 *			xtype: 'rangedatefield',
 *			dateType: 'datefield',
 *			//区间间隔，以天为单位。
 *			dateRange: 60,
 *			//起始日期组件的name属性。
 *			fromName: 'formDate',
 *			//终止日期组件的name属性。
 *			toName: 'toDate',
 *			//起始日期组件的初始值。
 *			//不允许为空
 *			disallowBlank: true
 *		},{
 *			fieldLabel: '时间',
 *			xtype: 'rangedatefield',
 *			dateType: 'timefield',
 *			//时间间隔，以分钟为单位。
 *			increment: 60,
 *			//起始日期组件的name属性。
 *			fromName: 'formTime',
 *			//终止日期组件的name属性。
 *			toName: 'toTime',
 *			//不允许为空
 *			disallowBlank: true
 *		}]
 *	});
 *	</code></pre>
 */
Ext.define('Deppon.form.field.RangeDateField', {
	extend: 'Ext.form.FieldContainer',
	alias: ['widget.rangeDateField','widget.rangedatefield'],
	alternateClassName: ['Deppon.form.RangeDateField'],
    /**
     *	@cfg {String} dateType
     *	区间段组件的子组件的类型，默认为Extjs框架原生DateField组件。
     */
	dateType: 'datefield',
    /**
     *	@cfg {String} fromName
     *	起始日期组件name属性。
     */
	fromName: '',
    /**
     *	@cfg {String} toName
     *	终止日期组件name属性。
     */
	toName: '',
    /**
     *	@cfg {Boolean} showDefaultFlag
     *	是否提供初始值功能。
     */
	showDefaultFlag: true,
    /**
     *	@cfg {String} fromValue
     *	起始日期组件初始值。
     */
	fromValue: '',
    /**
     *	@cfg {String} toValue
     *	终止日期组件初始值。
     */
	toValue: '',
    /**
     *	@cfg {Number} dateRange
	 *	起始日期和终止日期的间隔。单位：天。
     */
	dateRange: 0,
    /**
     *	@cfg {Boolean} time
     *	是否提供时间选择功能。
     */
	time: true,
    /**
     *	@cfg {Number} increment
     *	区间段类型为TimeField时，时间的增量，以分为单位。
     */
	increment: 30,
    /**
     *	@cfg {String} fieldId
     *	当区间段类型为my97datetimefield组件时，需配置此属性。
     *
     *	此属性为my97datetimefield组件渲染的Dom ID属性，建议配置成复杂的字符串。
     */
	fieldId: '',
    /**
     *	@cfg {Boolean} allowFromBlank
     *	起始日期组件非空标识。true不显示非空标识，false显示非空标识。
	 *	FOSS系统中，非空标识为组件前端的红色*号。
     */
	allowFromBlank: true,
    /**
     *	@cfg {Boolean} allowToBlank
     *	终止日期组件非空标识。true不显示非空标识，false显示非空标识。
	 *	FOSS系统中，非空标识为组件前端的红色*号。
     */
	allowToBlank: true,
    /**
     *	@cfg {Boolean} disallowBlank
     *	是否允许值为空，默认允许为空。
     */
	disallowBlank: false,
    /**
     *	@cfg {Boolean} fromEditable
     *	起始日期组件是否允许编辑，默认允许编辑。
     */
	fromEditable: true,
    /**
     *	@cfg {Boolean} toEditable
     *	终止日期组件是否允许编辑，默认允许编辑。
     */
	toEditable: true,
    /**
     *	@cfg {Boolean} editable
     *	是否允许编辑，默认允许编辑。
     *	此属性使起始日期组件和终止日期组件都为可编辑的。可以被单独的可编辑配置fromEditable和toEditable覆盖。
     */
	editable: true,
    /**
     *	@cfg {String} rangeFlag
     *	起始日期组件和终止日期组件间隔Text。
     */
	rangeFlag: '&nbsp;&nbsp;To&nbsp;&nbsp;',
	/**
	 * @private
     * 组件初始化方法。
     */
	initComponent: function() {
		var me = this;
		me.layout = 'column';
		me.initContainer();
		me.callParent(arguments);
		if(me.showDefaultFlag) {
			me.on('boxready', this.setDefaultValue, this, {single: true});
		}
	},
	/**
	 * @private
     * 初始化区间容器。
     */
	initContainer: function() {
		var me = this,
			fristField, toField, secondField,
			firstDateConfig, secondDateConfig,
			hasDateRange;
		me.dateType = me.dateType || 'datefield';
		if(me.disallowBlank) {
			me.allowFromBlank = false;
			me.allowToBlank = false;
		}
		if(!me.editable) {
			me.fromEditable = false;
			me.toEditable = false;
		}
		fristField = {
			itemId: 'first',
			xtype: me.dateType,
			name: me.fromName,
			allowBlank: me.allowFromBlank,
			editable: me.fromEditable,
			columnWidth: .5
		};
		toField = {
			xtype: 'label',
			html: me.rangeFlag,
			style: {
				textAlign: 'center',
				marginTop: '5px'
			}
		};
		secondField = {
			itemId: 'second',
			xtype: me.dateType,
			name: me.toName,
			allowBlank: me.allowToBlank,
			editable: me.toEditable,
			columnWidth: .5
		};
		hasDateRange = typeof(me.dateRange) == 'number' && me.dateRange != 0;
		switch (me.dateType) {
			case "my97datetimefield":
			case "datetimefield_date97":
				me.fieldId = me.fieldId || '';
				firstDateConfig = hasDateRange ? {
					el: me.fieldId + "_first-inputEl",
					startDate: me.fromValue,
					errDealMode: 1,
					maxDate: "#F{$dp.$D('"+me.fieldId+"_second-inputEl')}",
					minDate: "#F{$dp.$D('"+me.fieldId+"_second-inputEl', {d: -" + me.dateRange + "})}"
				} : {
					el: me.fieldId + "_first-inputEl",                        
					startDate: me.fromValue,                                  
					errDealMode: 1,                                           
					maxDate: "#F{$dp.$D('" + me.fieldId + "_second-inputEl')}"
				};
				secondDateConfig = hasDateRange ? {
					el: me.fieldId + "_second-inputEl",
					startDate: me.toValue,
					errDealMode: 1,
					minDate: "#F{$dp.$D('"+me.fieldId+"_first-inputEl')}",
					maxDate: "#F{$dp.$D('"+me.fieldId+"_first-inputEl', {d: " + me.dateRange + "})}"
				} : {
					el: me.fieldId + "_second-inputEl",
					startDate: me.toValue,
					errDealMode: 1,
					minDate: "#F{$dp.$D('" + me.fieldId + "_first-inputEl')}"
				};
				Ext.apply(fristField, {
					id: me.fieldId+"_first",
					time: me.time,
					dateConfig: firstDateConfig
				});
				Ext.apply(secondField, {
					id: me.fieldId+"_second",
					time: me.time,
					dateConfig: secondDateConfig
				});
				break;
			case "datefield":
				me.format = me.format || 'Y-m-d';
				Ext.apply(fristField, {
					format: me.format,
					value: me.fromValue,
					listeners: {
						select: {
							fn: function(field, value) {
								field.up().child('#second').setMinValue(value);
								if(hasDateRange) {
									var maxValue = Ext.Date.add(value, Ext.Date.DAY, me.dateRange);
									field.up().child('#second').setMaxValue(maxValue);
								}
							}
						},
						scope: me
					}
				});
				Ext.apply(secondField, {
					format: me.format,
					value: me.toValue,
					listeners: {
						select: {
							fn: function(field, value) {
								field.up().child('#first').setMaxValue(value);
								if(hasDateRange) {
									var minValue = Ext.Date.add(value, Ext.Date.DAY, me.dateRange);
									field.up().child('#first').setMaxValue(minValue);
								}
							}
						}
					}
				});
				break;
			case "timefield":
				me.increment = me.increment || 30;
				me.format = me.format || 'H: i';
				Ext.apply(fristField, {
					format: me.format,
					value: me.fromValue,
					increment: me.increment,
					listeners: {
						select: {
							fn: function(field, value) {
								field.up().child('#second').setMinValue(value);
							}
						}
					}
				});
				Ext.apply(secondField, {
					format: me.format,
					value: me.toValue,
					increment: me.increment,
					listeners: {
						select: {
							fn: function(field, value) {
								field.up().child('#first').setMaxValue(value);
							}
						}
					}
				});
		}
		me.items = [ fristField, toField, secondField ];
	},
	/**
	 * @method
     * 设置区间段组件初始化值。
     * 
     * 注：目前仅在区间类型为my97datetimefield组件时有效。
     */
	setDefaultValue: function() {
		var me  = this;
		if(me.dateType == "my97datetimefield" || me.dateType == "datetimefield_date97") {
			if(!Ext.isEmpty(me.fromValue)) {
				Ext.get(me.fieldId + "_first-inputEl").dom.value = me.fromValue;
			}
			if(!Ext.isEmpty(me.toValue)) {
				Ext.get(me.fieldId + "_second-inputEl").dom.value = me.toValue;
			}
		}
	}
});

//读取文件RowExpander.js
/**
 * @class Deppon.grid.plugin.RowExpander
 * @extends Ext.AbstractPlugin
 * <p>表格行可展开的插件</p>
 * <p>行体内容的类型绑定是通过在行体内容定义的类中实现一个bindData的方法，通过该方法进行行体中组件的数据绑定。
 * 在行体内的组件可以通过superGrid得到行体所在的表格，还可以通过record得到绑定到组件中的记录。</p>
 * <p>代码示例</p>
 * <pre><code>
 * @example 
 * Ext.onReady(function(){
 *   Ext.QuickTips.init();
 *   Ext.define('dpap.User.GridDetail',{
 *       extend: 'Ext.form.Panel',
 *       id: 'dpap.User.GridDetail_Id',
 *       title:'用户详细信息',
 *       frame: true,
 *       hidden : false,
 *       defaultType : 'textfield',
 *       layout:'column',
 *       align: 'stretch',
 *       height:'300',
 *       defaults: {
 *           readOnly : true,
 *           anchor: '90%',
 *           labelWidth: 40
 *       },
 *       bindData : function(record){
 *           this.loadRecord(record);
 *       },
 *       initComponent: function(config){
 *           var me = this,
 *               cfg = Ext.apply({}, config);
 *           me.items = [{
 *               name: 'name',
 *               fieldLabel: '姓名',
 *               allowBlank:false,
 *               columnWidth : 0.25
 *           },{
 *               name: 'email',
 *               fieldLabel: '邮件',
 *               allowBlank:false,
 *               columnWidth : 0.25
 *           },{
 *               name: 'phone',
 *               fieldLabel: '电话',
 *               allowBlank:false,
 *               columnWidth : 0.25
 *           }];
 *           me.callParent([cfg]);
 *       }
 *   });  
 *   Ext.define('dpap.model.user',{
 *       extend: 'Ext.data.Model',
 *       fields: [
 *           {name: 'name',     type: 'string'},
 *           {name: 'phone',    type: 'string'},
 *           {name: 'email',   type: 'string'}
 *       ]
 *   });
 *   Ext.create('Ext.data.Store', {
 *       storeId:'simpsonsStore',
 *       model:'dpap.model.user',
 *       data:{'items':[
 *           { 'name': 'Lisa',  "email":"lisa@simpsons.com",  "phone":"555-111-1224" },
 *           { 'name': 'Bart',  "email":"bart@simpsons.com",  "phone":"555-222-1234"  },
 *           { 'name': 'Homer', "email":"home@simpsons.com",  "phone":"555-222-1244"  },
 *           { 'name': 'Marge', "email":"marge@simpsons.com", "phone":"555-222-1254"  }
 *       ]},
 *       proxy: {
 *           type: 'memory',
 *           reader: {
 *               type: 'json',
 *               root: 'items'
 *           }
 *       }
 *   });
 *   Ext.create('Ext.grid.Panel', {
 *       title: 'rowexpander demo',
 *       frame: true,
 *       store: Ext.data.StoreManager.lookup('simpsonsStore'),
 *       plugins: [{
 *           ptype: 'rowexpander',
 *           rowsExpander: false,
 *           rowBodyElement : 'dpap.User.GridDetail'
 *       }],
 *       columns: [
 *           { text: '姓名', dataIndex: 'name',flex:1 },
 *           { text: '邮件', dataIndex: 'email',flex:1 },
 *           { text: '电话', dataIndex: 'phone',flex:1 }
 *       ],
 *       height: 400,
 *       width: 400,
 *       renderTo:Ext.getBody()
 *   });
 * });
 * </pre></code>
 */
Ext.define('Deppon.grid.plugin.RowExpander', {
	extend: 'Ext.AbstractPlugin',
	requires: ['Ext.grid.feature.RowBody', 'Ext.grid.feature.RowWrap'],
	alias: 'plugin.rowexpander',
	alternateClassName: ['Deppon.grid.RowExpander'],
	
	/**
	 * @cfg {Ext.XTemplate/Ext.Template/String/String[]} rowBodyTpl
	 * 一个 {@link Ext.Template}, {@link Ext.XTemplate} 或一个字符串的数组定义的显示模板. 该属性与'rowBodyElement'属性只能配置一个
	 */
	rowBodyTpl: null,
	
	/**
	 * @cfg {Boolean} rowsExpander
	 * 定义行展开模式,设置是否多行展开。
	 * 为true表示多行展开,false表示单行展开.默认为true
	 */
	rowsExpander: true,
	
	/**
	 * @cfg {Boolean} expandOnEnter
	 * 设置是否回车展开。
	 *
	 * 如果设置为false，则回车不展开；如果设置为true，则回车展开。默认为true。
	 */
	expandOnEnter: true,
	
	/**
	 * @cfg {Boolean} expandOnDblClick
	 * 设置是否双击展开。
	 *
	 * 如果设置为false，则双击不展开；如果设置为true，则双击展开。默认为true。
	 */
	expandOnDblClick: true,
	
	/**
	 * @cfg {Boolean} expandOnDblClick
	 * 设置是否显示行展开的图示。
	 *
	 * 如果设置为false，则不显示；如果设置为true，则显示。默认为false。
	 */
	header: false,
	
	/**
	 * @cfg {String} rowBodyElement
	 * 设置行展开内定义的组件类名。该属性与'rowBodyTpl'属性只能配置一个
	 * 为了实现展开行内组件数据的动态绑定，在Grid的信息预览的数据时，一般由某一行的record对象，
	 * 在执行binData方法的时候，有以下参数可以使用(和grid的Column的renderer属性的参数相同)</br>
	 * a)	record 当前行的record对象</br>
	 * b)	grid 当前展开表格对象</br>
	 * c)	rowBodyElement当前展开行内的组件对象
	 */
	rowBodyElement: null,
	
	/**
	 * @private
	 * 先前展开行的行号。
	 */
	beforeRowIndex: null,
	
	/**
	 * @private
	 * 展开行组件Id与组件对象的map缓存集合。
	 */
	elementIdMap: null,
	rowBodyTrSelector: '.x-grid-rowbody-tr',
	rowBodyHiddenCls: 'x-grid-row-body-hidden',
	rowCollapsedCls: 'x-grid-row-collapsed',
	
	/**
	 * @method
	 * 通过行的记录对象（record），获得展开行的行体信息
	 * @param {Ext.data.Model} record 行的记录对象.
	 * @return 展开行的行体信息
	 */
	getRowBodyElement: function (record) {
		if (this.rowBodyElement == null || Ext.isEmpty(record)) {
			return;
		}
		return this.elementIdMap.get(record.internalId + '-rowbody-element');
	},
	
	/**
	 * @method
	 * 获得表格展开行的行信息
	 * @return 被展开的行信息(如果是单行展开则返回一个展开行信息，如果是多行展开则返回展开行信息数组)
	 */
	getExpendRow: function () {
		if (this.rowBodyElement == null) {
			return;
		}
		var view = this.view,
		nodes = view.getNodes();
		expendRows = new Array();
		for (var index = 0; index < nodes.length; index++) {
			var rowNode = nodes[index],
			row = Ext.get(rowNode);
			if (!row.hasCls(this.rowCollapsedCls)) {
				expendRows.push(row);
			}
		}
		if (this.rowsExpander == false) {
			if (expendRows.length == 1) {
				return expendRows[0];
			} else {
				return null;
			}
		}
		return expendRows;
	},
	
	/**
	 * @method
	 * 通过行信息获得行绑定的记录
	 * @param 展开行的行体信息对象 rowNode.
	 * @return {Ext.data.Model} 行绑定的记录
	 */
	getRowRecord: function (rowNode) {
		if (this.rowBodyElement == null || Ext.isEmpty(rowNode)) {
			return;
		}
		var view = this.view,
		record = this.view.getRecord(rowNode);
		return record;
	},
	
	/**
	 * @method
	 * 获得表格内展开行的行体信息
	 * @return 展开行的行体信息(如果是单行展开则返回一个行体信息，如果是多行展开则返回行体信息数组)
	 */
	getExpendRowBody: function () {
		var expendRows = this.getExpendRow(),
		expendRowBodys = new Array();
		if (this.rowsExpander == false) {
			var record = this.getRowRecord(expendRows);
			var rowBody = this.getRowBodyElement(record);
			return rowBody;
		}
		for (var index = 0; index < expendRows.length; index++) {
			var record = this.getRowRecord(expendRows[index]);
			var rowBody = this.getRowBodyElement(record);
			expendRowBodys.push(rowBody);
		}
		return expendRowBodys;
	},
	
	renderer: function (value, metadata, record, rowIdx, colIdx) {
		if (colIdx === 0) {
			metadata.tdCls = 'x-grid-td-expander';
		}
		return '<div class="x-grid-row-expander">&#160;</div>';
	},
	
	constructor: function () {
		this.callParent(arguments);
		this.elementIdMap = new Ext.util.HashMap();
		var grid = this.getCmp();
		this.recordsExpanded = {};
		if (!this.rowBodyTpl && !this.rowBodyElement) {
			Ext.Error.raise("'rowBodyTpl' and 'rowBodyElement' of A property must configuration.");
		}
		var features = null;
		if (this.rowBodyTpl) {
			var rowBodyTpl = Ext.create('Ext.XTemplate', this.rowBodyTpl);
			features = [{
				ftype: 'rowbody',
				rowBodyTpl: this.rowBodyTpl,
				columnId: this.getHeaderId(),
				recordsExpanded: this.recordsExpanded,
				rowBodyHiddenCls: this.rowBodyHiddenCls,
				rowCollapsedCls: this.rowCollapsedCls,
				getAdditionalData: this.getRowBodyFeatureData,
				getRowBodyContents: function (data) {
					return rowBodyTpl.applyTemplate(data);
				}
			}, {
				ftype: 'rowwrap'
			}];
		}
		if (this.rowBodyElement) {
			features = [{
				ftype: 'rowbody',
				rowBodyElement: this.rowBodyElement,
				columnId: this.getHeaderId(),
				recordsExpanded: this.recordsExpanded,
				rowBodyHiddenCls: this.rowBodyHiddenCls,
				rowCollapsedCls: this.rowCollapsedCls,
				getAdditionalData: this.getRowBodyFeatureData,
				getRowBodyContents: function (data) {
					return "<div id='" + data + "' class='x-row-embedded'></div>";
				}
			}, {
				ftype: 'rowwrap'
			}];
		}
		if (grid.features) {
			grid.features = features.concat(grid.features);
		} else {
			grid.features = features;
		}
	},
	
	init: function (grid) {
		var me = this;
		this.callParent(arguments);
		if (this.header) {
			grid.headerCt.insert(0, this.getHeaderConfig());
		}
		grid.on('render', this.bindView, this, {
			single: true
		});
		grid.getView().on('refresh', function (store, eOpts) {
			me.cleanElementIdMap();
		}, this);
	},
	
	cleanElementIdMap: function () {
		if (this.elementIdMap == null) {
			return;
		}
		var view = this.view,
		ds = view.store;
		for (var index = 0; index < ds.data.length; index++) {
			rowIdx = ds.getAt(index),
			rowNode = view.getNode(rowIdx),
			row = Ext.get(rowNode);
			if (row != null && !row.hasCls(this.rowCollapsedCls)) {
				row.addCls(this.rowCollapsedCls);
			}
		}
		if(this.elementIdMap.length==0){
			return;
		}
		for (var i = 0; i < this.elementIdMap.getKeys().length; i++) {
			cleanComponent(this.elementIdMap.getValues()[i]);
			var header = Ext.ComponentManager.get(this.elementIdMap.getKeys()[i] + '_header');
			cleanComponent(header);
			Ext.ComponentManager.unregister(header);
			header = null;
		}
		this.elementIdMap.clear();
		this.beforeRowIndex = null;
	},
	
	getHeaderId: function () {
		if (!this.headerId) {
			this.headerId = Ext.id();
		}
		return this.headerId;
	},
	
	getRowBodyFeatureData: function (data, idx, record, orig, view) {
		var o = Ext.grid.feature.RowBody.prototype.getAdditionalData.apply(this, arguments),
			id = this.columnId;
		o.rowBodyColspan = o.rowBodyColspan;
		if (this.rowBodyTpl) {
			o.rowBody = this.getRowBodyContents(data);
		}
		if (this.rowBodyElement) {
			o.rowBody = this.getRowBodyContents(record.internalId + "-rowbody");
		}
		o.rowCls = this.recordsExpanded[record.internalId] ? '' : this.rowCollapsedCls;
		o.rowBodyCls = this.recordsExpanded[record.internalId] ? '' : this.rowBodyHiddenCls;
		o[id + '-tdAttr'] = ' valign="top" rowspan="1" height="28" ';
		if (orig[id + '-tdAttr']) {
			o[id + '-tdAttr'] += orig[id + '-tdAttr'];
		}
		return o;
	},
	
	bindView: function () {
		var view = this.getCmp().getView(),
		viewEl;
		if (!view.rendered) {
			view.on('render', this.bindView, this, {
				single: true
			});
		} else {
			viewEl = view.getEl();
			if (this.expandOnEnter) {
				this.keyNav = Ext.create('Ext.KeyNav', viewEl, {
					'enter': this.onEnter,
					scope: this
				});
			}
			if (this.expandOnDblClick) {
				view.on('itemdblclick', this.onDblClick, this);
			}
			this.view = view;
		}
	},
	
	onEnter: function (e) {
		var view = this.view,
			ds = view.store,
			sm = view.getSelectionModel(),
			sels = sm.getSelection(),
			ln = sels.length,
			i = 0,
			rowIdx;
		for (; i < ln; i++) {
			rowIdx = ds.indexOf(sels[i]);
			this.toggleRow(rowIdx);
		}
	},
	
	toggleRow: function (rowIdx) {
		if (!this.rowsExpander) {
			if (this.beforeRowIndex != null && this.beforeRowIndex != rowIdx) {
				var beforeRowNode = this.view.getNode(this.beforeRowIndex),
				beforeRow = Ext.get(beforeRowNode),
		beforeNextBd = Ext.get(beforeRow).down(this.rowBodyTrSelector),
		beforeRecord = this.view.getRecord(beforeRowNode);
				beforeRow.addCls(this.rowCollapsedCls);
				beforeNextBd.addCls(this.rowBodyHiddenCls);
				if (beforeRecord != null) {
					this.recordsExpanded[beforeRecord.internalId] = false;
					this.view.fireEvent('collapsebody', beforeRowNode, beforeRecord, beforeNextBd.dom);
				}
			}
			this.beforeRowIndex = rowIdx;
		}
		//###############################################
		var rowNode = this.view.getNode(rowIdx),
			row = Ext.get(rowNode),
			nextBd = Ext.get(row).down(this.rowBodyTrSelector),
			record = this.view.getRecord(rowNode),
			grid = this.getCmp(),
			rowBodyId = record.internalId + '-rowbody',
			rowBodyElementId = record.internalId + '-rowbody-element';
		if (row.hasCls(this.rowCollapsedCls)) {
			row.removeCls(this.rowCollapsedCls);
			nextBd.removeCls(this.rowBodyHiddenCls);
			this.recordsExpanded[record.internalId] = true;
			this.view.fireEvent('expandbody', rowNode, record, nextBd.dom);
			if (this.rowBodyElement && !this.elementIdMap.containsKey(rowBodyElementId)) {
				var rowBodyElement = Ext.create(this.rowBodyElement, {
					id: rowBodyElementId
				});
				Ext.Object.merge(rowBodyElement, {
					superGrid: grid,
					record: record
				});
				if (rowBodyElement && (typeof rowBodyElement.setTipData) != 'undefined') {
					rowBodyElement.setTipData(record);
				}
				//增加父表格列拖动的时候，表格展开中的表格不会随着被动
				rowBodyElement.on('resize',
				function (rowBodyEl, width, height, eOpts) {
					rowBodyEl.getEl().on('DOMSubtreeModified',

					function (e, htmlEl, eOpts) {
						var extEl = Ext.get(htmlEl);
						var el = extEl.select('table.' + Ext.baseCSSPrefix + 'grid-table-resizer');
						el.each(function (childEl, c, index) {
							if (childEl.hasCls(Ext.baseCSSPrefix + 'grid-table-resizer')) {
								childEl.removeCls(Ext.baseCSSPrefix + 'grid-table-resizer');
							}
						});
					});
				});
				rowBodyElement.render(rowBodyId);
				this.elementIdMap.add(rowBodyElement.id, rowBodyElement);
			}
			var rowBodyElement = this.elementIdMap.get(rowBodyElementId);
			//在定义行体的时候，装入的对象要如果要进行数据的bind，可以通过实现这个方法，进行实现
			if (rowBodyElement && (typeof rowBodyElement.bindData) != 'undefined') {
				rowBodyElement.bindData(record, grid, rowBodyElement);
			}
		} else {
			row.addCls(this.rowCollapsedCls);
			nextBd.addCls(this.rowBodyHiddenCls);
			this.recordsExpanded[record.internalId] = false;
			this.view.fireEvent('collapsebody', rowNode, record, nextBd.dom);
			var rowBodyElement = this.elementIdMap.get(rowBodyElementId);
			//在定义行体的时候，装入的对象如果在收缩的时候，要进行数据的unbind，可以通过实现这个方法，进行实现
			if (rowBodyElement && (typeof rowBodyElement.unbindData) != 'undefined') {
				rowBodyElement.unbindData(record, grid, rowBodyElement);
			}
		}
		var tab = grid.up('tabpanel');
		if(tab){
			tab.doComponentLayout();
		}
	},
	
	onDblClick: function (view, record, rowIdx, recordIndex, e) {
		this.toggleRow(rowIdx);
	},
	
	getHeaderConfig: function () {
		var me = this,
			toggleRow = Ext.Function.bind(me.onDblClick, me),
			selectRowOnExpand = me.selectRowOnExpand;
		return {
			id: this.getHeaderId(),
			width: 24,
			sortable: false,
			resizable: false,
			draggable: false,
			hideable: false,
			menuDisabled: true,
			cls: Ext.baseCSSPrefix + 'grid-header-special',
			renderer: function (value, metadata) {
				metadata.tdCls = Ext.baseCSSPrefix + 'grid-cell-special';
				return '<div class="' + Ext.baseCSSPrefix + 'grid-row-expander">&#160;</div>';
			},
	
			processEvent: function (type, view, cell, recordIndex, cellIndex, e) {
				if (type == "mousedown" && e.getTarget('.x-grid-row-expander')) {
					var row = e.getTarget('.x-grid-row');
					toggleRow(view, cell, row, recordIndex, e);
					return selectRowOnExpand;
				}
			}
		};
	}
});

//读取文件SimplePaging.js
/**
 *	@docauthor 曾宪涛 <rxjh2ht@163.com>
 *
 *	此组件为简单分页组件。
 *
 *	通常应用于combobox分页工具栏上和宽度比较小的gird上。
 *
 *	此组件仅实现基础的上一页、下一页功能。
 *
 *	# 组件示例
 *	<pre><code>
 *	@example
 *	Ext.define('ForumThread', {
 *      extend: 'Ext.data.Model',
 *      fields: [
 *          'title', 'forumtitle', 'forumid', 'username',
 *          {name: 'replycount', type: 'int'},
 *          {name: 'lastpost', mapping: 'lastpost', type: 'date', dateFormat: 'timestamp'},
 *          'lastposter', 'excerpt', 'threadid'
 *      ],
 *      idProperty: 'threadid'
 *  });
 *  var store = Ext.create('Ext.data.Store', {
 *		autoLoad: true,
 *      pageSize: 5,
 *      model: 'ForumThread',
 *      proxy: {
 *          type: 'jsonp',
 *          url: 'http://www.sencha.com/forum/topics-browse-remote.php',
 *          reader: {
 *              root: 'topics',
 *              totalProperty: 'totalCount'
 *          }
 *      }
 *  });
 *	Ext.create('Ext.grid.Panel', {
 *      width: 520,
 *      height: 200,
 *      title: '简单分页组件示例',
 *      store: store,
 *      disableSelection: true,
 *      loadMask: true,
 *		autoScroll: true,
 *		columnLines: true,
 *      frame: false,
 *      columns:[{
 *          text: "Topic",
 *          dataIndex: 'title',
 *          flex: 1,
 *          sortable: false
 *      },{
 *          text: "Author",
 *          dataIndex: 'username',
 *          width: 100,
 *          hidden: true,
 *          sortable: true
 *      },{
 *          text: "Replies",
 *          dataIndex: 'replycount',
 *          width: 70,
 *          align: 'right',
 *          sortable: true
 *      },{
 *         text: "Last Post",
 *         dataIndex: 'lastpost',
 *         width: 150,
 *         sortable: true
 *      }],
 *		dockedItems: [{
 *			xtype: 'simplepaging',
 *			store: store,
 *			dock: 'bottom'
 *		}],
 *      renderTo: Ext.getBody()
 *  });
 *	</code></pre>
 **/
Ext.define('Deppon.paging.SimplePaging', {
	extend: 'Ext.toolbar.Toolbar',
	alias: 'widget.simplepaging',
	alternateClassName: ['Deppon.SimplePaging'],
    mixins: {
        bindable: 'Ext.util.Bindable'    
    },
	/**
     * @cfg {Boolean} prependButtons
     * 设置在简单分页栏添加自定义按钮的位置。
     *
     * 如果设置为false，用户自定义的Items位置将位于分页的前面；反正则位于后面。默认为false。
     */
    prependButtons: false,
	/**
     * @cfg {String} displayMsg
     * 显示总页数的Text。
     */
	displayMsg: 'Displaying {0}',
	/**
     * @cfg {String} emptyMsg
     * Store无记录时的显示Text。
     */
    emptyMsg: 'No data to display',
	/**
     * @cfg {String} prevText
     * 显示上一页的tip信息。
     */
    prevText : 'Previous Page',
	/**
     * @cfg {String} nextText
     * 显示下一页的tip信息。
     */
    nextText : 'Next Page',
	/**
	 * @private
     * 获取简单分页组件的Items。
     */
    getPagingItems: function() {
        var me = this;
		return ['->', {
			xtype: 'tbtext', 
			itemId: 'displayItem'
		}, {
            itemId: 'prev',
            tooltip: me.prevText,
            overflowText: me.prevText,
			iconCls: 'deppon_icons_tbarpaging_pre',
            disabled: true,
            handler: me.movePrevious,
            scope: me
        }, {
            itemId: 'next',
            tooltip: me.nextText,
            overflowText: me.nextText,
			iconCls: 'deppon_icons_tbarpaging_next',
            disabled: true,
            handler: me.moveNext,
            scope: me
        }]
    },
	/**
	 * @private
     * 组件初始化方法。
     *
     * 主要执行初始化Items、添加监听事件、绑定Store等操作。
     */
	initComponent: function() {
        var me = this,
            pagingItems = me.getPagingItems(),
            userItems   = me.items || me.buttons || [];
        if (me.prependButtons) {
            me.items = userItems.concat(pagingItems);
        } else {
            me.items = pagingItems.concat(userItems);
        }
        delete me.buttons;
        me.callParent();
        me.addEvents(
            'change',
            'beforechange'
        );
        me.on('beforerender', me.onLoad, me, {single: true});
        me.bindStore(me.store || 'ext-empty-store', true);
    },
	/**
	 * @method
     * 更新简单分页组件的显示信息（更新总页数显示）。
     */
    updateInfo: function() {
        var me = this,
            store = me.store,
            pageData = me.getPageData(),
            displayItem = me.child('#displayItem'),
            count, msg;
        if (displayItem) {
            count = store.getCount();
            if (count === 0) {
                msg = me.emptyMsg;
            } else {
                msg = Ext.String.format(
                    me.displayMsg,
					pageData.pageCount
                );
            }
            displayItem.setText(msg);
        }
    },
	/**
	 * @private
     * 简单分页工具栏渲染之前的监听方法。
     */
    onLoad: function() {
        var me = this,
            pageData,
            currPage,
            pageCount,
            afterText,
            count,
            isEmpty;
        count = me.store.getCount();
        isEmpty = count === 0;
        if (!isEmpty) {
            pageData = me.getPageData();
            currPage = pageData.currentPage;
            pageCount = pageData.pageCount;
        } else {
            currPage = 0;
            pageCount = 0;
        }
        Ext.suspendLayouts();
        me.child('#prev').setDisabled(currPage === 1  || isEmpty);
        me.child('#next').setDisabled(currPage === pageCount  || isEmpty);
        me.updateInfo();
        Ext.resumeLayouts(true);
        if (me.rendered) {
            me.fireEvent('change', me, pageData);
        }
    },
	/**
	 * @method
     * 获取分页相关信息。
     * @return {Object} 返回插分页相关信息。
	 * Object主要包含的信息如下：
	 *
     * 	{Number} total Store总数。
	 *
     * 	{Number} currentPage 当前页数。
	 *
     * 	{Number} pageCount 总页数。
	 *
     * 	{Number} fromRecord 起始页数。
	 *
     * 	{Number} toRecord 目标页面。
     */
    getPageData: function() {
        var store = this.store,
            totalCount = store.getTotalCount();
        return {
            total: totalCount,
            currentPage: store.currentPage,
            pageCount: Math.ceil(totalCount / store.pageSize),
            fromRecord: ((store.currentPage - 1) * store.pageSize) + 1,
            toRecord: Math.min(store.currentPage * store.pageSize, totalCount)
        };
    },
	/**
	 * @method
     * 移动至上一页，实现点击上一页的功能。
	 */
    movePrevious: function() {
        var me = this,
            prev = me.store.currentPage - 1;
        if (prev > 0) {
            if (me.fireEvent('beforechange', me, prev) !== false) {
                me.store.previousPage();
            }
        }
    },
	/**
	 * @method
     * 移动至下一页，实现点击下一页的功能。
	 */
    moveNext: function() {
        var me = this,
            total = me.getPageData().pageCount,
            next = me.store.currentPage + 1;
        if (next <= total) {
            if (me.fireEvent('beforechange', me, next) !== false) {
                me.store.nextPage();
            }
        }
    },
	/**
	 * @method
     * 获取Store的监听事件。
	 */
    getStoreListeners: function() {
        return {
            load: this.onLoad
        };
    },
	/**
	 * @private
     * 删除绑定的Store。
	 */
    unbind: function(store) {
        this.bindStore(null);
    },
	/**
	 * @private
     * 绑定Store。
	 */
    bind: function(store) {
        this.bindStore(store);
    },
	/**
	 * @private
     * 销毁方法，此处主要执行删除绑定Store的功能{@link #unbind}。
	 */
    onDestroy: function() {
        this.unbind();
        this.callParent();
    }
});

//读取文件StandardPaging.js
/**
 *	@docauthor 曾宪涛 <rxjh2ht@163.com>
 *
 *	此组件为标准的分页组件。
 *
 *	# 组件示例
 *	<pre><code>
 *	@example
 *	Ext.define('ForumThread', {
 *      extend: 'Ext.data.Model',
 *      fields: [
 *          'title', 'forumtitle', 'forumid', 'username',
 *          {name: 'replycount', type: 'int'},
 *          {name: 'lastpost', mapping: 'lastpost', type: 'date', dateFormat: 'timestamp'},
 *          'lastposter', 'excerpt', 'threadid'
 *      ],
 *      idProperty: 'threadid'
 *  });
 *  var store = Ext.create('Ext.data.Store', {
 *		autoLoad: true,
 *      pageSize: 5,
 *      model: 'ForumThread',
 *      proxy: {
 *          type: 'jsonp',
 *          url: 'http://www.sencha.com/forum/topics-browse-remote.php',
 *          reader: {
 *              root: 'topics',
 *              totalProperty: 'totalCount'
 *          }
 *      }
 *  });
 *	Ext.create('Ext.grid.Panel', {
 *      width: 580,
 *      height: 190,
 *      title: '标准分页组件示例',
 *      store: store,
 *      disableSelection: true,
 *      loadMask: true,
 *		autoScroll: true,
 *		columnLines: true,
 *      frame: false,
 *      columns:[{
 *          text: "Topic",
 *          dataIndex: 'title',
 *          flex: 1,
 *          sortable: false
 *      },{
 *          text: "Author",
 *          dataIndex: 'username',
 *          width: 100,
 *          hidden: true,
 *          sortable: true
 *      },{
 *          text: "Replies",
 *          dataIndex: 'replycount',
 *          width: 70,
 *          align: 'right',
 *          sortable: true
 *      },{
 *         text: "Last Post",
 *         dataIndex: 'lastpost',
 *         width: 150,
 *         sortable: true
 *      }],
 *		dockedItems: [{
 *			xtype: 'standardpaging',
 *			store: store,
 *			dock: 'bottom'
 *		}],
 *      renderTo: Ext.getBody()
 *  });
 *	</code></pre>
 **/
Ext.define('Deppon.paging.StandardPaging', {
	extend: 'Ext.toolbar.Toolbar',
	alias: 'widget.standardpaging',
	alternateClassName: ['Deppon.StandardPaging'],
    mixins: {
        bindable: 'Ext.util.Bindable'
    },
	/**
	 * @private
     * @cfg {Number} randomId
     * 随机数，用于生成分页栏中元素的ID。
     */
    randomId: 0,
	cls: 'depponPaging',
	/**
     * @cfg {Boolean} prependButtons
     * 设置在简单分页栏添加自定义按钮的位置。
     *
     * 如果设置为false，用户自定义的Items位置将位于分页的前面；反正则位于后面。默认为false。
     */
    prependButtons: false,
	/**
     * @cfg {Boolean} totalCountFlag
     * 设置是否显示记录数。
     *
     * 如果设置为false，则只显示总页数；如果设置为true，则显示总页数和总条数。默认为false。
     */
	totalCountFlag: false,
	/**
     * @cfg {String} displayMsg
     * 显示总页数的Text。
     */
	displayMsg: 'Total: {0} page(s)',
	/**
     * @cfg {String} displayMsg
     * 显示总页数和总条数的Text。
     */
	displayExtralMsg: 'Total: {0} page(s), {1} records',
	/**
     * @cfg {String} emptyMsg
     * Store无记录时的显示Text。
     */
	emptyMsg: 'No data to display',
	/**
     * @cfg {String} firstText
     * 显示第一页的text和tip信息。
     */
	firstText: 'First Page',
	/**
     * @cfg {String} prevText
     * 显示上一页的text和tip信息。
     */
    prevText: 'Previous Page',
	/**
     * @cfg {String} nextText
     * 显示下一页的text和tip信息。
     */
    nextText: 'Next Page',
	/**
     * @cfg {String} lastText
     * 显示最后一页的text和tip信息。
     */
    lastText: 'Last Page',
	/**
	 * @private
     * 获取标准分页组件的Items。
     */
    getPagingItems: function() {
		return ['->', {
			xtype: 'tbtext',
			baseCls: 'x-standardpaging-total',
			itemId: 'displayItem'
		}];
    },
	/**
	 * @private
     * 组件初始化方法。
     *
     * 主要执行初始化Items、添加监听事件、绑定Store等操作。
     */
	initComponent: function() {
        var me = this,
            pagingItems = me.getPagingItems(),
            userItems = me.items || me.buttons || [];
		me.randomId = Math.random();
        if (me.prependButtons) {
            me.items = userItems.concat(pagingItems);
        } else {
            me.items = pagingItems.concat(userItems);
        }
        delete me.buttons;
        me.callParent();
        me.addEvents(
            'change',
            'beforechange'
        );
        me.on('beforerender', me.onLoad, me, {single: true});
        me.bindStore(me.store || 'ext-empty-store', true);
    },
	/**
	 * @method
     * 更新简单分页组件的显示信息（更新总页数显示）。
     */
    updateInfo: function() {
        var me = this,
            displayItem = me.child('#displayItem'),
            store = me.store,
            pageData = me.getPageData(),
            count, msg;
        if (displayItem) {
            count = store.getCount();
            if (count === 0) {
                msg = me.emptyMsg;
            } else {
				if(!me.totalCountFlag) {
					msg = Ext.String.format(
						me.displayMsg,
						pageData.pageCount
					);
				} else {
					msg = Ext.String.format(
						me.displayExtralMsg,
						pageData.pageCount,
						pageData.total
					);
				}
            }
            displayItem.setText(msg);
        }
    },
	/**
	 * @private
     * 创建标准分页的分页元素。
     */
	createPageView: function(pageList, currentPage) {
		var me = this,
			total = me.store.getTotalCount(),
			pageCount = Math.ceil(total/me.store.pageSize);
		for(var i=0; i<pageList.length; i++) {
			var pageItem = Ext.get('pageItem_' + me.randomId + '_' + i);
			if(pageItem) {
				pageItem.removeCls('deppon-paging-item-visible');
				pageItem.addCls('deppon-paging-item-display');
                pageItem.dom.innerHTML = (pageList[i] == '') ? '' : (pageList[i]);
                if(pageList[i] != '' && pageList[i] != '...' && pageList[i] != currentPage) {
                    pageItem.dom.style.color = '#385F95';
					pageItem.dom.style.display = 'block';
                    pageItem.dom.style.cursor = 'pointer';
					if((pageList[i]=='首页'&&currentPage==1) || (pageList[i]=='上一页'&&currentPage==1) || (pageList[i]=='下一页'&&currentPage==pageCount) || (pageList[i]=='尾页'&&currentPage==pageCount)) {
						pageItem.dom.style.cursor = 'default';
	                    pageItem.dom.style.color = '#000000';
					}
                }  else {
                    pageItem.dom.style.cursor = 'default';
					pageItem.dom.style.display = 'block';
	                if (pageList[i] == currentPage) {
	                    pageItem.dom.style.color = '#000000';
	                } else if( pageList[i] == '...') {
	                    pageItem.dom.style.color = '#385F95';
					} else if(pageList[i] == '') {
						pageItem.dom.style.display = 'none';
					}
                }
                pageItem.removeAllListeners();
                if(pageList[i]=='...' || pageList[i]=='' || pageList[i]==currentPage) {
                    pageItem.on('click', function() {});
                } else if(typeof(pageList[i]) == 'number') {
                    pageItem.on('click', function(e, k) {
                        var currentPage = parseInt(k.textContent || k.innerText);
						if(me.fireEvent('beforechange', me, currentPage) !== false){
							me.store.loadPage(currentPage);
						}
                    }, this, {single: true});
                } else if(pageList[i] == '首页') {
					if(currentPage != 1) pageItem.on('click', me.moveFirst, me);
				} else if(pageList[i] == '上一页') {
					pageItem.on('click', me.movePrevious, me);
				} else if(pageList[i] == '下一页') {
					pageItem.on('click', me.moveNext, me);
				} else if(pageList[i] == '尾页') {
					if(currentPage != pageCount) pageItem.on('click', me.moveLast, me);
				}
            }
		}
	},
	/**
	 * @private
     * 根据当前页，获取分页元素列表。
     */
	getPageList: function(currPage) {
		var me = this,
			store = me.store,
			currentPage = currPage;
			totalCount = store.getTotalCount(),
			pageCount = Math.ceil(totalCount / store.pageSize),
			pageList = [];
		
		if(pageCount <= 7) {
            for(var i=0; i<13; i++) {
				if(i==0) { pageList.push('首页'); }
				else {
					if(i==1) pageList.push('上一页');
					if(i<=pageCount) pageList.push(i);
					else if(i==pageCount+1) pageList.push('下一页');
					else if(i==pageCount+2) pageList.push('尾页');
					else if(i>pageCount+2) pageList.push('');
				}
            }
        } else {
            if(currentPage <= 5) {
                for(var i=0; i<13; i++) {
					if(i==0) { pageList.push('首页'); }
					else {
						if(i==1) pageList.push('上一页');
						if(i<8) pageList.push(i);
						else if(i==9) pageList.push('...');
						else if(i==10) pageList.push(pageCount);
						else if(i==11) pageList.push('下一页');
						else if(i==12) pageList.push('尾页');
					}
                }
            } else if(currentPage > 5 && currentPage < (pageCount-4)) {
                for(var i=0; i<13; i++) {
					if(i==0) pageList.push('首页');
					else if(i==1) pageList.push('上一页');
                    else if(i==2) pageList.push(1);
                    else if(i==3 || i==9) pageList.push('...');
                    else if(i>3 && i<9) pageList.push(currentPage-(6-i));
                    else if(i==10) pageList.push(pageCount);
					else if(i==11) pageList.push('下一页');
					else if(i==12) pageList.push('尾页');
                }
            } else if(currentPage >= (pageCount-4)) {
                for(var i=0; i<13; i++) {
					if(i==0) pageList.push('首页');
					else if(i==1) pageList.push('上一页');
                    else if(i==2) pageList.push(1);
                    else if(i==3) pageList.push('...');
                    else if(i>3 && i<11) pageList.push(pageCount-(10-i));
					else if(i==11) pageList.push('下一页');
					else if(i==12) pageList.push('尾页');
                }
            }
        };
        me.createPageView(pageList, currentPage);	
    },
	/**
	 * @private
     * 标准分页工具栏渲染之前的监听方法。
     */
    onLoad: function() {
        var me = this,
            pageData,
            currPage,
            count,
            totalCount,
            isEmpty;
        count = me.store.getCount();
        currPage = me.store.currentPage;
    	totalCount = me.store.getTotalCount();
        isEmpty = count === 0;
        if (!isEmpty) {
            pageData = me.getPageData();
        } else {
			if(totalCount>0  && currPage>1) {
        		me.store.loadPage(currPage - 1);
				currPage = 0;
        	} else {
            	currPage = 0;
        	}
        }
        Ext.suspendLayouts();
        me.updateInfo();
		if(currPage != 0) {
			me.getPageList(currPage);
		} else {
			for(var i=0; i<13; i++) {
				if(Ext.fly('pageItem_' + me.randomId + '_' + i)) {
					Ext.fly('pageItem_' + me.randomId + '_' + i).dom.style.display = 'none';
				} else {
					me.add({
						tag: 'span',
						type: 'text',
						cls: 'deppon-paging-item-visible',
						id: 'pageItem_' + me.randomId + '_' + i
					});
				}
			};
		};
        Ext.resumeLayouts(true);
        if (me.rendered) {
            me.fireEvent('change', me, pageData);
        }
    },
	/**
	 * @method
     * 获取分页相关信息。
     * @return {Object} 返回插分页相关信息。
	 * Object主要包含的信息如下：
	 *
     * 	{Number} total Store总数。
	 *
     * 	{Number} currentPage 当前页数。
	 *
     * 	{Number} pageCount 总页数。
	 *
     * 	{Number} fromRecord 起始页数。
	 *
     * 	{Number} toRecord 目标页面。
     */
    getPageData: function() {
        var store = this.store,
            totalCount = store.getTotalCount();
        return {
            total: totalCount,
            currentPage: store.currentPage,
            pageCount: Math.ceil(totalCount / store.pageSize),
            fromRecord: ((store.currentPage - 1) * store.pageSize) + 1,
            toRecord: Math.min(store.currentPage * store.pageSize, totalCount)
        };
    },
	/**
	 * @method
     * 移动至第一页，实现点击首页的功能。
	 */
    moveFirst: function() {
        var me = this;
		if (me.fireEvent('beforechange', me, 1) !== false) {
			me.store.loadPage(1);
		}
    },
	/**
	 * @method
     * 移动至上一页，实现点击上一页的功能。
	 */
    movePrevious: function() {
        var me = this,
            prev = me.store.currentPage - 1;
        if (prev > 0) {
            if (me.fireEvent('beforechange', me, prev) !== false) {
                me.store.previousPage();
            }
        }
    },
	/**
	 * @method
     * 移动至下一页，实现点击下一页的功能。
	 */
    moveNext: function() {
        var me = this,
            total = me.getPageData().pageCount,
            next = me.store.currentPage + 1;
        if (next <= total) {
            if (me.fireEvent('beforechange', me, next) !== false) {
                me.store.nextPage();
            }
        }
    },
	/**
	 * @method
     * 移动至最后一页，实现点击尾页的功能。
	 */
    moveLast: function() {
        var me = this,
            last = me.getPageData().pageCount;
		if (me.fireEvent('beforechange', me, last) !== false) {
			me.store.loadPage(last);
		}
    },
	/**
	 * @method
     * 获取Store的监听事件。
	 */
	beforeLoad: function() {
		var me = this,
			grid = me.up('grid');
		if( grid != undefined && grid.getSelectionModel().getSelectionMode() === "MULTI") {
			var sm = grid.getSelectionModel(),
				view = sm.views[0],
				checkHd, headerCt;
			if(view) {
				headerCt = view.headerCt;
				if(headerCt) {
					checkHd = headerCt.child('gridcolumn[isCheckerHd]');
					if(checkHd && checkHd.el) {
						if(checkHd.el.hasCls('x-grid-hd-checker-on')) {
							checkHd.el.removeCls('x-grid-hd-checker-on');
						}
					}
				}
			}
		}
	},
    getStoreListeners: function() {
        return {
		beforeload: this.beforeLoad,
            	load: this.onLoad
        };
    },
	/**
	 * @private
     * 删除绑定的Store。
	 */
    unbind: function(store) {
        this.bindStore(null);
    },
	/**
	 * @private
     * 绑定Store。
	 */
    bind: function(store) {
        this.bindStore(store);
    },
	/**
	 * @private
     * 销毁方法，此处主要执行删除绑定Store的功能{@link #unbind}。
	 */
    onDestroy: function() {
        this.unbind();
        this.callParent();
    }
});

//读取文件TabCloseMenu.js
/**
 * 插件添加关闭上下文菜单选项卡。注意,菜单中有配置选项卡可闭。因此,在删除所有菜单的时候，配置了不可关闭的菜单，是不会关闭的。
 */
Ext.define('Deppon.tab.TabCloseMenu', {
    alias: 'plugin.tabclosemenu',

    mixins: {
        observable: 'Ext.util.Observable'
    },

    /**
     * @cfg {String} closeTabText
     * 关闭当前的文本显示
     */
    closeTabText: 'Close Tab',

    /**
     * @cfg {Boolean} showCloseOthers
     * 是否显示"关闭其它"的菜单.
     */
    showCloseOthers: true,

    /**
     * @cfg {String} closeOtherTabsText
     * 关闭其它的文本显示.
     */
    closeOthersTabsText: 'Close Other Tabs',

    /**
     * @cfg {Boolean} showCloseAll
     * 是否显示"关闭所有"的菜单
     */
    showCloseAll: true,

    /**
     * @cfg {String} closeAllTabsText
     * <p>关闭所有的文本显示.
     */
    closeAllTabsText: 'Close All Tabs',

    extraItemsHead: null,

    extraItemsTail: null,

    //public
    constructor: function (config) {
        this.addEvents(
            'aftermenu',
            'beforemenu');

        this.mixins.observable.constructor.call(this, config);
    },

    init : function(tabpanel){
        this.tabPanel = tabpanel;
        this.tabBar = tabpanel.down("tabbar");

        this.mon(this.tabPanel, {
            scope: this,
            afterlayout: this.onAfterLayout,
            single: true
        });
    },

    onAfterLayout: function() {
        this.mon(this.tabBar.el, {
            scope: this,
            contextmenu: this.onContextMenu,
            delegate: 'div.x-tab'
        });
    },

    onBeforeDestroy : function(){
        Ext.destroy(this.menu);
        this.callParent(arguments);
    },

    // private
    onContextMenu : function(event, target){
        var me = this,
            menu = me.createMenu(),
            disableAll = true,
            disableOthers = true,
            tab = me.tabBar.getChildByElement(target),
            index = me.tabBar.items.indexOf(tab);

        me.item = me.tabPanel.getComponent(index);
        menu.child('*[text="' + me.closeTabText + '"]').setDisabled(!me.item.closable);

        if (me.showCloseAll || me.showCloseOthers) {
            me.tabPanel.items.each(function(item) {
                if (item.closable) {
                    disableAll = false;
                    if (item != me.item) {
                        disableOthers = false;
                        return false;
                    }
                }
                return true;
            });

            if (me.showCloseAll) {
                menu.child('*[text="' + me.closeAllTabsText + '"]').setDisabled(disableAll);
            }

            if (me.showCloseOthers) {
                menu.child('*[text="' + me.closeOthersTabsText + '"]').setDisabled(disableOthers);
            }
        }

        event.preventDefault();
        me.fireEvent('beforemenu', menu, me.item, me);

        menu.showAt(event.getXY());
    },

    createMenu : function() {
        var me = this;

        if (!me.menu) {
            var items = [{
                text: me.closeTabText,
                scope: me,
                handler: me.onClose
            }];

            if (me.showCloseOthers) {
                items.push({
                    text: me.closeOthersTabsText,
                    scope: me,
                    handler: me.onCloseOthers
                });
            }

            if (me.showCloseAll) {
                items.push({
                    text: me.closeAllTabsText,
                    scope: me,
                    handler: me.onCloseAll
                });
            }

            if (me.extraItemsHead) {
                items = me.extraItemsHead.concat(items);
            }

            if (me.extraItemsTail) {
                items = items.concat(me.extraItemsTail);
            }

            me.menu = Ext.create('Ext.menu.Menu', {
                items: items,
                listeners: {
                    hide: me.onHideMenu,
                    scope: me
                }
            });
        }

        return me.menu;
    },

    onHideMenu: function () {
        var me = this;

        me.item = null;
        me.fireEvent('aftermenu', me.menu, me);
    },

    onClose : function(){
        this.tabPanel.remove(this.item);
    },

    onCloseOthers : function(){
        this.doClose(true);
    },

    onCloseAll : function(){
        this.doClose(false);
    },

    doClose : function(excludeActive){
        var items = [];

        this.tabPanel.items.each(function(item){
            if(item.closable){
                if(!excludeActive || item != this.item){
                    items.push(item);
                }
            }
        }, this);

        Ext.each(items, function(item){
            this.tabPanel.remove(item);
        }, this);
    }
});

//读取文件TabScrollerMenu.js
/**
 * @class Deppon.tab.TabScrollerMenu
 * <p>tab标签滚动显示和鼠标右击关闭插件</p>
 * <p><u>代码示例</u></p>
 * <pre><code>
 * @example
 * Ext.onReady(function () {
 *    var tabPanel = Ext.create("Ext.tab.Panel", {
 *        title: "tabscrollermenu  demo ",
 *        frame: true,
 *        height: 200,
 *        width: 400,
 *        activeTab: 0, 
 *        plugins: [{
 *           ptype: 'tabscrollermenu',
 *           id:'OFB_menu',
 *           maxText  : 40,
 *           pageSize : 100
 *       },{
 *           ptype: 'tabclosemenu',
 *           closeTabText: '关闭',
 *           closeOthersTabsText: '关闭其他',
 *           closeAllTabsText: '关闭所有'
 *       }],
 *       items: [{
 *           tabConfig:{width:100},
 *           title: "主页"
 *       }],
 *       buttons: [{
 *           text: "添加标签页",
 *           handler: addTab
 *       }],
 *       renderTo: Ext.getBody(),
 *   });
 *   function addTab() {
 *       var index = tabPanel.items.length ;
 *       var tab = tabPanel.add({
 *           title: "tab标签页" + index,
 *           tabConfig:{width:100},
 *           closable: true  
 *       });
 *       tabPanel.setActiveTab(tab);
 *   }
 * });
 * </pre></code>
 */
Ext.define('Deppon.tab.TabScrollerMenu', {
	alias: 'plugin.tabscrollermenu',
	uses: ['Ext.menu.Menu'],
	/**
	 * @cfg {Number} pageSize
	 * pageSize 显示标签数量
	 */
	pageSize: 10,
	
	/**
	 * @cfg {Number} maxText
	 * maxText 显示最大文本数量
	 */
	maxText: 15,
	
    /**
     * @cfg {String} menuPrefixText 菜单按钮文本的前缀.
     */
    menuPrefixText: 'Items',
    
    constructor: function(config) {
        config = config || {};
        Ext.apply(this, config);
    },
    
    //private
    init: function(tabPanel) {
        var me = this;

        Ext.apply(tabPanel, me.parentOverrides);
        me.tabPanel = tabPanel;

        tabPanel.on({
            render: function() {
                me.tabBar = tabPanel.tabBar;
                me.layout = me.tabBar.layout;
                me.layout.overflowHandler.handleOverflow = Ext.Function.bind(me.showButton, me);
                me.layout.overflowHandler.clearOverflow = Ext.Function.createSequence(me.layout.overflowHandler.clearOverflow, me.hideButton, me);
            },
            single: true
        });
    },

    showButton: function() {
        var me = this,
            result = Ext.getClass(me.layout.overflowHandler).prototype.handleOverflow.apply(me.layout.overflowHandler, arguments);

        if (!me.menuButton) {
            me.menuButton = me.tabBar.body.createChild({
                cls: Ext.baseCSSPrefix + 'tab-tabmenu-right'
            }, me.tabBar.body.child('.' + Ext.baseCSSPrefix + 'box-scroller-right'));
            me.menuButton.addClsOnOver(Ext.baseCSSPrefix + 'tab-tabmenu-over');
            me.menuButton.on('click', me.showTabsMenu, me);
        }
        me.menuButton.show();
        result.reservedSpace += me.menuButton.getWidth();
        return result;
    },

    hideButton: function() {
        var me = this;
        if (me.menuButton) {
            me.menuButton.hide();
        }
    },

    /**
     * 返回当前页面的标签数量
     * @return {Number} this.pageSize 当前页面的标签数量.
     */
    getPageSize: function() {
        return this.pageSize;
    },
    
    /**
     * 设置当前页面的标签数据
     * @param {Number} pageSize 标签数量
     */
    setPageSize: function(pageSize) {
        this.pageSize = pageSize;
    },
    
    
    /**
     * 返回当前页面的最大标签数量
     * @return {Number} this.maxText 返回当前页面记录的最大文本长度.
     */
    getMaxText: function() {
        return this.maxText;
    },
    
    /**
     * 设置最大文本长度
     * @param {Number} t 最大文本长度.
     */
    setMaxText: function(t) {
        this.maxText = t;
    },
    
    /**
     * 返回菜单按钮文本的前缀
     * @return {String} this.menuPrefixText 当前菜单按钮文本的前缀.
     */
    getMenuPrefixText: function() {
        return this.menuPrefixText;
    },
    
    /**
     * 设置当前菜单按钮文本的前缀.
     * @param {String} t 前缀字符串.
     */
    setMenuPrefixText: function(t) {
        this.menuPrefixText = t;
    },

    showTabsMenu: function(e) {
        var me = this;

        if (me.tabsMenu) {
            me.tabsMenu.removeAll();
        } else {
            me.tabsMenu = Ext.create('Ext.menu.Menu');
            me.tabPanel.on('destroy', me.tabsMenu.destroy, me.tabsMenu);
        }

        me.generateTabMenuItems();

        var target = Ext.get(e.getTarget());
        var xy = target.getXY();

        //Y param + 24 pixels
        xy[1] += 24;

        me.tabsMenu.showAt(xy);
    },

    // private
    generateTabMenuItems: function() {
        var me = this,
            tabPanel = me.tabPanel,
            curActive = tabPanel.getActiveTab(),
            totalItems = tabPanel.items.getCount(),
            pageSize = me.getPageSize(),
            numSubMenus = Math.floor(totalItems / pageSize),
            remainder = totalItems % pageSize,
            i, curPage, menuItems, x, item, start, index;

        if (totalItems > pageSize) {

            // Loop through all of the items and create submenus in chunks of 10
            for (i = 0; i < numSubMenus; i++) {
                curPage = (i + 1) * pageSize;
                menuItems = [];

                for (x = 0; x < pageSize; x++) {
                    index = x + curPage - pageSize;
                    item = tabPanel.items.get(index);
                    menuItems.push(me.autoGenMenuItem(item));
                }

                me.tabsMenu.add({
                    text: me.getMenuPrefixText() + ' ' + (curPage - pageSize + 1) + ' - ' + curPage,
                    menu: menuItems
                });
            }
            // remaining items
            if (remainder > 0) {
                start = numSubMenus * pageSize;
                menuItems = [];
                for (i = start; i < totalItems; i++) {
                    item = tabPanel.items.get(i);
                    menuItems.push(me.autoGenMenuItem(item));
                }

                me.tabsMenu.add({
                    text: me.menuPrefixText + ' ' + (start + 1) + ' - ' + (start + menuItems.length),
                    menu: menuItems
                });

            }
        }
        else {
            tabPanel.items.each(function(item) {
                me.tabsMenu.add(me.autoGenMenuItem(item));
            });
        }
    },

    // private
    autoGenMenuItem: function(item) {
        var maxText = this.getMaxText(),
            text = Ext.util.Format.ellipsis(item.title, maxText);

        return {
            text: text,
            handler: this.showTabFromMenu,
            scope: this,
            disabled: item.disabled,
            tabToShow: item,
            iconCls: item.iconCls
        };
    },

    // private
    showTabFromMenu: function(menuItem) {
        this.tabPanel.setActiveTab(menuItem.tabToShow);
    }
});

//读取文件TipColumn.js
/**
 * @class Deppon.grid.column.TipColumn
 * @extends Ext.grid.column.Column
 * <p>实现gird中列内容的富提示(传入自定义组件显示在提示浮动窗体中)功能</p>
 * <p>Tip内容的类型绑定是通过在tip内容定义的类中实现一个bindData的方法，通过该方法进行tip中组件的数据绑定。</p>
 * <p><u>代码示例:</u></p>
 * <pre><code>
 * @example
 * Ext.create('Ext.data.Store', {
 *   storeId:'simpsonsStore',
 *   fields:['name', 'email', 'phone'],
 *   data:{'items':[
 *       { 'name': 'Lisa',  "email":"lisa@simpsons.com",  "phone":"555-111-1224"  },
 *       { 'name': 'Bart',  "email":"bart@simpsons.com",  "phone":"555-222-1234" },
 *       { 'name': 'Homer', "email":"home@simpsons.com",  "phone":"555-222-1244"  },
 *       { 'name': 'Marge', "email":"marge@simpsons.com", "phone":"555-222-1254"  }
 *   ]},
 *   proxy: {
 *       type: 'memory',
 *       reader: {
 *           type: 'json',
 *           root: 'items'
 *       }
 *   }
 * });  
 * Ext.create('Ext.grid.Panel', {
 *   title: 'tipcolumn  demo',
 *   frame:true,  
 *   store: Ext.data.StoreManager.lookup('simpsonsStore'),
 *   columns: [{ 
 *       header: 'Name',  
 *       dataIndex: 'name' 
 *   },{ 
 *       header: 'Email', 
 *       dataIndex: 'email', 
 *       flex: 1 ,
 *       xtype: 'tipcolumn', 
 *       tipConfig: {
 *           trackMouse: true,
 *           hideDelay: 500 
 *       },  
 *       tipBodyElement:'foss.textarea.tipcolumn' 
 *   },{ 
 *       header: 'Phone', 
 *       dataIndex: 'phone' 
 *   }],
 *   height: 200,
 *   width: 400,
 *   renderTo:Ext.getBody()
 *   });
 * Ext.define('foss.textarea.tipcolumn',{                    
 *   extend: 'Ext.form.Panel',                                       
 *   height: 100,                                                    
 *   width: 240,                                                     
 *   defaultType : 'textfield',                                       
 *   layout:'column',                                                   
 *   items:[{                                                        
 *       name:'email',                                               
 *       fieldLabel: '邮件',                                               
 *       labelSeparator:'',                                            
 *       hideMode: 'visibility',                                       
 *       readOnly:true                                                 
 *   },{                                                        
 *       name:'name',                                               
 *       fieldLabel: '姓名',                                               
 *       labelSeparator:'',                                            
 *       hideMode: 'visibility',                                       
 *       readOnly:true                                                 
 *   },{                                                        
 *       name:'phone',                                               
 *       fieldLabel: '电话',                                               
 *       labelSeparator:'',                                            
 *       hideMode: 'visibility',                                       
 *       readOnly:true                                                 
 *   }],                                                             
 *   bindData : function(record,value,metadata,store,view){          
 *       if(Ext.isEmpty(record.data.email)){                         
 *           return false;                                               
 *       };                                                            
 *       this.getForm().loadRecord(record);                            
 *   },                                                              
 *   constructor: function(config){                                  
 *       var me = this,                                                
 *           cfg = Ext.apply({}, config);                                  
 *       me.callParent([cfg]);                                         
 *   }                                                               
 * });     
 * </code></pre> 
 */   
Ext.define('Deppon.grid.column.TipColumn', {
	extend: 'Ext.grid.column.Column',
	alias: ['widget.tipcolumn'],
	
	/**
	 * @cfg {String} tipBodyElement
	 * 设置Tip的Body中显示的组件类名
	 * 为了实现tip内组件数据的动态绑定，在Grid的信息预览的数据时，一般由某一行的record对象，在执行binData方法的时候，有以下参数可以使用(和grid的Column的renderer属性的参数相同)</br>
	 * a)	record 当前行的record对象</br>
	 * b)	value 当前单元格的值</br>
	 * c)	metadata 当前单元格的元数据</br>
	 * d)	store 当前grid的store对象</br>
	 * e)	view 当前view
	 */
	tipBodyElement: null,
	
	/**
	 * @cfg {Ext.tip.Tooltip} tipConfig
	 * Tip的具体配置:
	 * trackMouse: 是否随着鼠标移动
	 * hideDelay: Tip的隐藏延迟时间(单位:ms)
	 */
	tipConfig: null,
	
	/**
	 * @private
	 * @cfg tipBody
	 * tip内显示的对象
	 */
	tipBody: null,
	
	/**
	 * @private
	 * @cfg tip
	 * tip显示框对象
	 */
	tip: null,
	
	/**
	 * @private
	 * @cfg hasListener
	 * 是否已经绑定事件
	 */
	hasListener: false,
	
	addMouseOverLis: function(value, metadata, record, store, view) {
		var me = this;
		store.each(function(record, index, length) {
			var td = Ext.get(record.internalId+'-td');
			if(td==null){
				return;
			}
			td.on('mouseover',function( e, element, eOpts ) {			
				if(me.tipBody && (typeof me.tipBody.bindData) != 'undefined') {					
					var result = me.tipBody.bindData(record, value, metadata, store, view);					
					if(!Ext.isEmpty(result)) {					    
						if(!result) {					        
							return;					    
						}
					}				
				}				
				me.tip.showAt([e.browserEvent.pageX+10,e.browserEvent.pageY+10]);			
			});
		});
	},
	
	initComponent: function() {
		var me = this,
			customerRenderer = me.renderer;
		if(customerRenderer) {
			me.renderer = function(value, metadata, record, rowIndex, columnIndex, store, view) {
				value = customerRenderer(value, metadata, record, rowIndex, columnIndex, store, view);
				value = me.defaultRenderer(value, metadata, record, rowIndex, columnIndex, store, view);
				return value;
			};
		};
		me.defaultRenderer = function(value, metadata, record, rowIndex, columnIndex, store, view) {
			var tdId = record.internalId + '-td';
			if(Ext.isEmpty(me.tipBody)) {
				if(!Ext.isEmpty(me.tipBodyElement)){
					me.tipBody = Ext.create(me.tipBodyElement);
				}
			}
			if(me.tip==null) {
				if(me.tipConfig == null) {
					me.tipConfig = {};
				}
				if(!Ext.isEmpty(me.tipBody)) {
					me.tipConfig.items = [me.tipBody];
				}
				me.tip = Ext.create('Ext.tip.ToolTip', me.tipConfig);
			}
			metadata.tdAttr = "id=" +'"'+tdId+'"';
			if(!me.hasListener) {
				var evenObject,evenName;
				if(store.getProxy().type=='ajax' || store.getProxy().type=='jsonp' || store.getProxy().type=='direct') {
					evenObject = store;
					evenName = 'load';
				} else {
					// 修改之前view的事件 'viewready' 为 'refresh'
					evenObject = view;
					evenName = 'refresh';
				}
				evenObject.on(evenName, function(){
					me.addMouseOverLis(value, metadata, record, store, view);
				});
				view.up('grid').on('sortchange', function(){
					me.addMouseOverLis(value, metadata, record, store, view);
				});
				store.on('datachanged', function(){
					me.addMouseOverLis(value, metadata, record, store, view);
				});
				me.hasListener = true;
			}
			return value;
		};
		me.callParent(arguments);
	}
});  

//读取文件Toast.js
/**
 *	@docauthor 曾宪涛 <rxjh2ht@163.com>
 *
 *	冒泡提示功能与Extjs框架提示功能比较，前者无确定按钮，某些情况下用户体验较好。
 *
 *	并且冒泡提示功能提供显示时间的设置，可以根据需求设定显示的时间。
 *
 *	# 功能示例
 *	<pre><code>
 *	@example
 *	Ext.create('Ext.button.Button', {		
 *		frame: true,
 *		text: '冒泡提示示例',
 *		maxWidth: 200,
 *		height: 30,
 *		handler: function() {
 *			//冒泡提示
 *			Ext.ux.Toast.msg("提示", "程序出错！", "error", 2000);
 *		},
 *		renderTo : Ext.getBody()
 *	});
 *	</code></pre>
 */
Ext.ux.Toast = function() {
    var msgCt;
	function createBox(t, s, f) {
		var result;
		if(f == 'info') {
			result = ['<div class="msg msg_info">',
				'<div class="left"></div>',
				'<div class="right"><div class="title">'+ t +'</div><div class="textpanelstyle"><p>'+ s +'</p></div></div>',
				'<div class="bottom"></div>',
				'</div>'].join('');
		} else if(f == 'error'){
			result = ['<div class="msg msg_error">',
						'<div class="left"></div>',
						'<div class="right"><div class="title">'+ t +'</div><div class="textpanelstyle"><p>'+ s +'</p></div></div>',
						'<div class="bottom"></div>',
				'</div>'].join('');
		}
		 else {
				result = ['<div class="msg msg_waring">',
							'<div class="left"></div>',
							'<div class="right"><div class="title">'+ t +'</div><div class="textpanelstyle"><p>'+ s +'</p></div></div>',
							'<div class="bottom"></div>',
					'</div>'].join('');
			}
		return result;
    };
    return {
    	 /**
    	  * 冒泡提示的实现方法
		  * @param {String} title 标题
		  * @param {String} content 内容
		  * @param {String} flag 提示类型，一般提示或错误提示，默认为一般提示
		  * @param {number} delay 显示时间，单位毫秒
		  */
        msg: function(title, content, flag, delay) {
            if(!msgCt) {
                msgCt = Ext.DomHelper.insertFirst(document.body, {
					id: 'msg-div'
				}, true);
            }
			if(Ext.isEmpty(flag)) flag = 'ok';
			if(Ext.isEmpty(delay)) delay = 4000;
            var m = Ext.DomHelper.append(msgCt, {
				html: createBox(title, content, flag)
			}, true);
            m.hide();
            m.slideIn('t').ghost('t', { delay: delay, remove: true});
        },
		init: function() {
		
		}
	};
}();

//读取文件ZeroClipboardPlugin.js
/**
 *	@docauthor 曾宪涛 <rxjh2ht@163.com>
 *
 *	此插件封装ZeroClipboard，实现复制内容至剪切板的功能。
 *
 *	复制的内容提供三种获取方式，第一种直接传值，第二种通过表单域组件的name属性获取，第三者通过funcction的返回值获取。
 *
 *	ZeroClipboard利用Falsh实现复制至剪切板的功能，适用于大多数支持Flash的浏览器上，具有良好的浏览器兼容性。
 *
 *	# 插件示例
 *	<pre><code>
 *	@example
 *	Ext.create('Ext.window.Window', {
 *		title: '复制至剪切板插件',
 *		width: 400,	
 *		layout: 'fit',
 *		items:[{
 *			xtype: 'form',
 *			border: false,
 *			labelWidth: 80,
 *			items: [{
 *				xtype: 'textarea',
 *				fieldLabel: '内容',
 *				width: 280,
 *				margin: '20 10',
 *				name: 'remark',
 *				height: 100
 *			}]
 *		}],
 *		buttons: [{
 *			xtype : 'button',
 *			text: '复制',
 *			plugins: {
 *				ptype: 'zeroclipboardplugin',
 *				targetFun: function(component) {
 *					var button = component,
 *						textarea = button.up('window').down('form').down('textarea');
 *					return textarea.getValue();
 *				}
 *			}
 *		}]
 *	}).show();
 *	</code></pre>
 */
Ext.define('Deppon.ux.ZeroClipboardPlugin', {
	alias: 'plugin.zeroclipboardplugin',
	/**
     * @cfg {Oject} targetCmpName
     * 如果复制内容为表单域的value，且绑定ZeroClipboard的元素位于form内，则配置此属性为表单域的name属性即可。
     */
	targetCmpName: null,
	/**
     * @cfg {Function} targetFun
     * 如果复制内容为function返回的值，则配置此属性为function对象。
     */
	targetFun: null,
	/**
     * @cfg {String} targetValue
     * 如果复制内容为指定文本，则配置此属性为指定文本。
     */
	targetValue: null,
	/**
	 * @private
     * 构造函数，初始化此插件的配置项。
     */
	constructor: function(config) {
		var me = this;
		Ext.apply(me, config);
	},

	/**
	 * @private
     * 插件初始化方法，适用于Button组件上。在组件的initComponent方法执行完毕后调用。
     */
	init: function(component) {
		var me = this,
			jsLoader;
		me.component = component;
		jsLoader = new Deppon.JsLoader();		
		jsLoader.onSuccess = function () {
			me.initZeroClipboard();
		};
		jsLoader.load(Deppon.config.Dpap.RESOURCE_PATH + "/components/zeroclipboard/ZeroClipboard.js");
		component.on('mouseover', me.glueZeroClipboard, me);
	},

	/**
	 * @method
     * 初始化ZeroClipboard。
     * @return {Object} ZeroClipboard对象。
     */
	initZeroClipboard: function() {
		var me = this;
		me.clip = new ZeroClipboard.Client();
		me.clip.setHandCursor(true);
        	me.clip.addEventListener("mouseOver", Ext.bind(me.onMouseOver, me));
		me.clip.addEventListener('complete', function (client, text) {
			me.clip.hide();
			Ext.ux.Toast.msg("提示", "内容已经复制至剪切板");
		});
	},

	/**
	 * @method
     * 绑定ZeroClipboard于组件的Element对象上。
     * @return {Object} ZeroClipboard对象。
     */
	glueZeroClipboard: function() {
		var me = this;
		if(me.clip) {
			me.clip.destroy();
			me.clip.glue(me.component.getEl().id);
		}
		return me.clip;
	},
	/**
	 * @private
     * ZeroClipboard对象的mouseOver监听事件。鼠标移动时初始化ZeroClipboard对象的复制文本。
     */
	onMouseOver: function() {
		var me = this,
			value;
		if(me.targetCmpName) {
			var targetCmp = me.component.up('form').getForm().findField(me.targetCmpName);
			value = targetCmp.getValue();
		} else if(me.targetFun) {
			value = me.targetFun(me.component);
		} else if(me.targetValue) {
			value = me.targetValue;
		}
		me.clip.setText(value);
	},
	/**
	 * @private
     * 销毁插件相关的属性和对象。该方法会在组件销毁的时候自动调用。
     */
	destroy: function() {
		var me = this;
		me.component.clearListeners();
		me.clip.destroy();
		
		delete me.component;
		delete me.clip;
	}
});
